├── .github └── workflows │ └── build.yml ├── .gitignore ├── CMakeLists.txt ├── README.md ├── about.md ├── changelog.md ├── logo.png ├── mod.json └── src ├── CCTextInputNode.cpp ├── ChallengeNode.cpp ├── ChallengesPage.cpp ├── CharacterColorPage.cpp ├── CommentCell.cpp ├── CreatorLayer.cpp ├── CustomSongWidget.cpp ├── CustomizeObjectLayer.cpp ├── DailyLevelNode.cpp ├── DailyLevelPage.cpp ├── DemonFilterSelectLayer.cpp ├── DemonInfoPopup.cpp ├── EditLevelLayer.cpp ├── EditorPauseLayer.cpp ├── EditorUI.cpp ├── EndLevelLayer.cpp ├── FLAlertLayer.cpp ├── FriendRequestPopup.cpp ├── FriendsProfilePage.cpp ├── GJCommentListLayer.cpp ├── GJDropDownLayer.cpp ├── GJGarageLayer.cpp ├── GJGroundLayer.cpp ├── GJListLayer.cpp ├── GJMessageCell.cpp ├── GJScoreCell.cpp ├── GameToolbox.cpp ├── GauntletLayer.cpp ├── GauntletNode.cpp ├── GauntletSelectLayer.cpp ├── IDCheck.hpp ├── InfoLayer.cpp ├── ItemInfoPopup.cpp ├── LeaderboardsLayer.cpp ├── LevelAreaInnerLayer.cpp ├── LevelAreaLayer.cpp ├── LevelBrowserLayer.cpp ├── LevelCell.cpp ├── LevelEditorLayer.cpp ├── LevelInfoLayer.cpp ├── LevelLeaderboard.cpp ├── LevelListCell.cpp ├── LevelListLayer.cpp ├── LevelPage.cpp ├── LevelSearchLayer.cpp ├── LevelSelectLayer.cpp ├── LevelSettingsLayer.cpp ├── MenuGameLayer.cpp ├── MoreOptionsLayer.cpp ├── MoreSearchLayer.cpp ├── OptionsLayer.cpp ├── PauseLayer.cpp ├── PlayLayer.cpp ├── PlayerObject.cpp ├── ProfilePage.cpp ├── RewardsPage.cpp ├── SecretLayer.cpp ├── SecretLayer2.cpp ├── SecretLayer3.cpp ├── SecretLayer4.cpp ├── SecretLayer6.cpp ├── SecretRewardsLayer.cpp ├── SetGroupIDLayer.cpp ├── SetupMoveCommandPopup.cpp ├── ShardsPage.cpp ├── StarInfoPopup.cpp ├── UILayer.cpp └── main.cpp /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build Geode Mod 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: 7 | - "**" 8 | 9 | jobs: 10 | build: 11 | strategy: 12 | fail-fast: false 13 | matrix: 14 | config: 15 | - name: Windows 16 | os: windows-latest 17 | 18 | - name: macOS 19 | os: macos-latest 20 | 21 | - name: iOS 22 | os: macos-latest 23 | target: iOS 24 | 25 | - name: Android32 26 | os: ubuntu-latest 27 | target: Android32 28 | 29 | - name: Android64 30 | os: ubuntu-latest 31 | target: Android64 32 | 33 | name: ${{ matrix.config.name }} 34 | runs-on: ${{ matrix.config.os }} 35 | 36 | steps: 37 | - uses: actions/checkout@v4 38 | 39 | - name: Build the mod 40 | uses: geode-sdk/build-geode-mod@main 41 | with: 42 | combine: true 43 | target: ${{ matrix.config.target }} 44 | 45 | package: 46 | name: Package builds 47 | runs-on: ubuntu-latest 48 | needs: ['build'] 49 | 50 | steps: 51 | - uses: geode-sdk/build-geode-mod/combine@main 52 | id: build 53 | with: 54 | cli: nightly 55 | 56 | - uses: actions/upload-artifact@v4 57 | with: 58 | name: Build Output 59 | path: ${{ steps.build.outputs.build-output }} 60 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | # Macos be like 35 | **/.DS_Store 36 | 37 | # Cache files for Sublime Text 38 | *.tmlanguage.cache 39 | *.tmPreferences.cache 40 | *.stTheme.cache 41 | 42 | # Ignore build folders 43 | **/build 44 | **/build-win 45 | 46 | # Workspace files are user-specific 47 | *.sublime-workspace 48 | 49 | # ILY vscode 50 | **/.vscode 51 | .idea/ 52 | 53 | # clangd 54 | .cache/ 55 | 56 | # CLion 57 | /cmake-build-debug/ 58 | /cmake-build-minsizerel/ 59 | /cmake-build-release/ 60 | /cmake-build-relwithdebinfo/ 61 | 62 | # Visual Studio 63 | .vs/ -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.21) 2 | set(CMAKE_CXX_STANDARD 20) 3 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 4 | if ("${CMAKE_SYSTEM_NAME}" STREQUAL "iOS" OR IOS) 5 | set(CMAKE_OSX_ARCHITECTURES "arm64") 6 | else() 7 | set(CMAKE_OSX_ARCHITECTURES "arm64;x86_64") 8 | endif() 9 | set(CMAKE_CXX_VISIBILITY_PRESET hidden) 10 | 11 | project(NodeIDs VERSION 1.0.0) 12 | 13 | file(GLOB SOURCES 14 | src/*.cpp 15 | ) 16 | 17 | add_library(${PROJECT_NAME} SHARED ${SOURCES}) 18 | 19 | if (NOT DEFINED ENV{GEODE_SDK}) 20 | message(FATAL_ERROR "Unable to find Geode SDK! Please define GEODE_SDK environment variable to point to Geode") 21 | else() 22 | message(STATUS "Found Geode: $ENV{GEODE_SDK}") 23 | endif() 24 | 25 | add_subdirectory($ENV{GEODE_SDK} ${CMAKE_CURRENT_BINARY_DIR}/geode) 26 | 27 | setup_geode_mod(${PROJECT_NAME}) 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NodeIDs 2 | 3 | Standard node IDs for layers in Geode. 4 | 5 | Whenever you hook a layer in Geode and want to add stuff to it, in order to preserve compatability with other mods, you should always use node IDs rather than `objectAtIndex` calls. You need to depend on this mod to add the IDs. 6 | 7 | For more information, [read the docs page on IDs & Layouts](https://docs.geode-sdk.org/tutorials/nodetree). 8 | 9 | ## Using as a dependency 10 | 11 | Add the mod to your `mod.json`: 12 | 13 | ```json 14 | { 15 | "dependencies": [ 16 | { 17 | "id": "geode.node-ids", 18 | "version": "v1.10.0", 19 | "importance": "required" 20 | } 21 | ] 22 | } 23 | ``` 24 | 25 | **All the hooks in this mod have very low priority and as such should always be automatically run before your hooks.** However, if you want to ensure that IDs have been provided for your modified layer, call `NodeIDs::provideFor` (Geode function, not from this mod): 26 | 27 | ```cpp 28 | #include 29 | 30 | using namespace geode::prelude; 31 | 32 | class $modify(EditorUI) { 33 | bool init(LevelEditorLayer* lel) { 34 | if (!EditorUI::init(lel)) 35 | return false; 36 | 37 | NodeIDs::provideFor(this); 38 | 39 | return true; 40 | } 41 | }; 42 | ``` 43 | -------------------------------------------------------------------------------- /about.md: -------------------------------------------------------------------------------- 1 | # Node IDs 2 | 3 | Adds node IDs to layers for other mods to base their UI on, for preserving mod compatibility. See the [Github repository](https://github.com/geode-sdk/NodeIDs) for contributing. 4 | 5 | ## Why is this needed? 6 | Back in the ancient times we had to get objects by their indices - whenever you wanted to get the play button in the main menu, you had to know it was the 3rd button added there. 7 | 8 | At first this approach seemed to work well, but then several issues were found - what if a mod adds its own button before all others? What if it removes a button RobTop added? Now the 3rd button might be something completely different! 9 | 10 | Geode provides a solution - Node IDs. Now you don't need to bother with numbers at all! Instead you grab "play-button" and you're always guaranteed to get the correct button, even if some other mod completely reorganizes the main menu. -------------------------------------------------------------------------------- /changelog.md: -------------------------------------------------------------------------------- 1 | # Node IDs Changelog 2 | ## v1.20.0 3 | * Added IDs for sprites in EditorUI [#122](https://github.com/geode-sdk/NodeIDs/pull/122) 4 | * Fixed small visual bugs in Editor Delete Menu [#121](https://github.com/geode-sdk/NodeIDs/pull/121) 5 | 6 | ## v1.19.0 7 | * Added StarInfoPopup IDs [#115](https://github.com/geode-sdk/NodeIDs/pull/115) 8 | * Fixed incorrect controller sprite ID in GauntletSelectLayer 9 | * Fixed incorrect GJListLayer IDs in Level List view 10 | * Stopped using getChildBySpriteFrameName reimplementation in PauseLayer 11 | 12 | ## v1.18.0 13 | * Added FriendRequestPopup IDs 14 | * Added GJMessageCell IDs 15 | * Added LevelAreaLayer and SecretLayer3 and SecretLayer6 [#113](https://github.com/geode-sdk/NodeIDs/pull/113) 16 | * Fixed LevelPage IDs on non-level pages 17 | * Updated about.md 18 | 19 | ## v1.17.0 20 | * Added ItemInfoPopup [#111](https://github.com/geode-sdk/NodeIDs/pull/111) 21 | * Added SecretLayer and SecretLayer4 (2.0 vault and Chamber of Time) [#109](https://github.com/geode-sdk/NodeIDs/pull/109) 22 | * Fixed replaceInput not inheriting number input property [#110](https://github.com/geode-sdk/NodeIDs/pull/110) 23 | * Updated for GD 2.207 24 | 25 | ## v1.16.0 26 | * Added secret coin ID to LevelPage 27 | * Fixed a crash in LevelCell when Texture Loader fallback is used for the NCS logo 28 | * Fixed incorrect PlayLayer IDs when "Hide Playtest Text" setting is enabled 29 | * Improved LevelPage handling (adding nodes in init should no longer break the whole ID set) 30 | 31 | ## v1.15.0 32 | * Fixed an issue where some Private Servers would crash upon opening level comments 33 | * Improved GauntletNode IDs 34 | 35 | ## v1.14.1 36 | * Fixed CommentCell positioning on 4:3 displays 37 | 38 | ## v1.14.0 39 | * Added GauntletLayer IDs 40 | * Upgraded CommentCell for level comments 41 | 42 | ## v1.13.2 43 | * Fixed testmode label in main levels (PlayLayer) 44 | * Fixed crash when entering Gauntlets (macOS only) 45 | 46 | ## v1.13.1 47 | * Fixed improper assignment of claim-button in LevelListLayer 48 | 49 | ## v1.13.0 50 | * Added LevelListLayer 51 | * Added FriendsProfilePage 52 | * Added DemonInfoPopup 53 | * Added SecretLayer2 (thanks @MuhXd [#98](https://github.com/geode-sdk/NodeIDs/pull/98)) 54 | * Fixed parental control crash in InfoLayer while loading comments (thanks @SpaghettDev [#94](https://github.com/geode-sdk/NodeIDs/pull/94)) 55 | * Fixed ProfilePage positioning issues (name running away, info button misaligned after refresh) 56 | * Fixed some CustomizeObjectLayer issues (invisible clear text button, misalignments) 57 | * Fixed EditorPauseLayer issues (thanks @Alphalaneous [#95](https://github.com/geode-sdk/NodeIDs/pull/95)) 58 | 59 | ## v1.12.0 60 | 61 | * Bump Geode to v3.0.0-beta.1 62 | 63 | ## v1.11.0 64 | * Add IDs for all the EditButtonBars in EditorUI as well as for the edit buttons (move, rotate, etc.) 65 | 66 | ## v1.10.2 67 | * Rebuild the mod against updated bindings to fix macOS crashes 68 | 69 | ## v1.10.1 70 | * Temporarily remove LevelListLayer IDs as there were no layouts which will need to be added due to API breaks 71 | 72 | ## v1.10.0 73 | * Added CustomSongWidget (thanks @Fleeym) 74 | * Added ShardsPage, DemonFilterSelectLayer, LevelListLayer, LevelListCell & MoreSearchLayer (thanks @RayDeeUx, [#50](https://github.com/geode-sdk/NodeIDs/pull/50)) 75 | * Added GJGroundLayer & MenuGameLayer (thanks @TheSillyDoggo, [#56](https://github.com/geode-sdk/NodeIDs/pull/56)) 76 | * Tweak the positioning of `bottom-menu` in `CreatorLayer` (@acaruso-xx, [#53](https://github.com/geode-sdk/NodeIDs/pull/53)) 77 | 78 | ## v1.9.1 79 | * Updated EditorUI positioning logic to fix mod compatibility regressions 80 | 81 | ## v1.9.0 82 | * Added batch-layer for GJBaseGameLayer 83 | * Added GauntletSelectLayer (thanks @SpaghettDev and @ninXout) 84 | * Added GauntletNode, LevelPage, LevelSelectLayer and MoreOptionsLayer (thanks @SpaghettDev) 85 | * Added GJDropDownLayer and GJListLayer (thanks @kynex7510) 86 | * Added layouts and menus to LevelSearchLayer (thanks @Alphalaneous) 87 | * Added more PlayLayer IDs (thanks @Prevter) 88 | * Fixed EditorPauseLayer on mobile 89 | * Fixed EditorUI spacing 90 | * Fixed LevelInfoLayer misalignments 91 | 92 | ## v1.8.1 93 | - Fixed the rewards room misalignment on non-16:9 screens 94 | 95 | ## v1.8.0 96 | * charactercolorpage :D by @Weebifying in [#27](https://github.com/geode-sdk/NodeIDs/pull/27) 97 | * Implement SecretRewardsLayer (chest room) by @MaSp005 in [#28](https://github.com/geode-sdk/NodeIDs/pull/28) 98 | * LevelEditorLayer.cpp implemented and PlayLayer.cpp modified by @Termantita in [#31](https://github.com/geode-sdk/NodeIDs/pull/31) 99 | 100 | # v1.7.1 101 | - Removed UILayer Node ID from PlayLayer because mods depended on the default one 102 | 103 | # v1.7.0 104 | - Fixed EndLevelLayer IDs for newly collected coins 105 | - Fixed SetGroupIDLayer misalignments 106 | - Added OptionsLayer (thanks @Uproxide) 107 | - Added ChallengesPage (thanks @Uproxide) 108 | - Added PlayLayer (thanks @Prevter) 109 | - Added GJCommentListLayer 110 | 111 | # v1.6.1 112 | - Fixed EndLevelLayer crash on Mac 113 | 114 | # v1.6.0 115 | * Add `EndLevelLayer` IDs 116 | * Fix inputs not working in `SetGroupIDLayer` 117 | * Bump Geode version requirement to beta.20+ 118 | 119 | # v1.5.1 120 | - Fix `SetGroupIDLayer` being broken like most of the time 121 | 122 | # v1.5.0 123 | - Add `SetGroupIDLayer` IDs and layouts 124 | 125 | # v1.4.0 126 | - Fixed PauseLayer crashing on Mac 127 | - Added FLAlertLayer node IDs 128 | - Added LevelAreaInnerLayer node IDs 129 | 130 | # v1.3.0 131 | - Fix a crash in PauseLayer 132 | - Add CustomizeObjectLayer (not available for MacOS yet) 133 | 134 | # v1.2.4 135 | - Fixed duplicate ProfilePage menus 136 | - Fixed PauseLayer crash for fresh save files 137 | - Added macOS support 138 | 139 | # v1.2.3 140 | - Fixed MegaHack Level Edit issue >:( 141 | - Fixed LevelCell properly 142 | 143 | # v1.2.2 144 | - Fixed weekly level crashing 145 | 146 | # v1.2.1 147 | - Fixed editor pause menu crash on Windows yayy 148 | 149 | # v1.2.0 150 | Added IDs for: 151 | - EditorPauseLayer 152 | - EditorUI 153 | - GJGarageLayer 154 | - PauseLayer 155 | - UILayer 156 | 157 | Added logo 158 | 159 | # v1.1.4 160 | - Fixed an android32 specific crash in Saved Levels 161 | 162 | # v1.1.3 163 | - Added Android 2.205 support 164 | 165 | # v1.1.2 166 | - Fixed Android 6 compatibility 167 | - Fixed swapped buttons in LevelInfoLayer 168 | - Added bottom layout to GJScoreCell 169 | 170 | # v1.1.1 171 | - Fixed crashes in InfoLayer and LevelCell 172 | - Added missing LevelInfoLayer IDs 173 | 174 | # v1.1.0 175 | - Updated for 2.204 176 | - Many many more ids (thank you @Cvolton) 177 | 178 | # v1.0.0 179 | Initial release for Windows and Android, thanks to our lovely crewmate @cvolton. Adds ids for: 180 | 181 | - CommentCell 182 | - CreatorLayer 183 | - DailyLevelNode 184 | - DailyLevelPage 185 | - EditLevelLayer 186 | - LevelBrowserLayer 187 | - LevelInfoLayer 188 | - LevelSearchLayer -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geode-sdk/NodeIDs/911607876c8f7842c3e239356495185c28396162/logo.png -------------------------------------------------------------------------------- /mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "geode": "4.4.0", 3 | "gd": { 4 | "win": "2.2074", 5 | "android": "2.2074", 6 | "mac": "2.2074", 7 | "ios": "2.2074" 8 | }, 9 | "version": "v1.20.1", 10 | "id": "geode.node-ids", 11 | "name": "Node IDs", 12 | "developer": "Geode Team", 13 | "description": "Standard node IDs to be used by other mods.", 14 | "links": { 15 | "source": "https://github.com/geode-sdk/NodeIDs" 16 | }, 17 | "issues": { 18 | "url": "https://github.com/geode-sdk/NodeIDs/issues", 19 | "info": "If you encounter an issue related to node ID mismatches, please report it to the GitHub issues page." 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/CCTextInputNode.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // This doesn't actually add any IDs since those aren't needed for 4 | // CCTextInputNode where everything is accessible through members. 5 | // This is to fix the effects of the epic mistake of Cocos2d inventing 6 | // ignoreAnchorPointForPosition which causes the content size of 7 | // text input nodes to be way off 8 | 9 | // struct $modify(CCTextInputNode) { 10 | // bool init(float width, float height, const char* caption, const char* thonburi, int maxCharCount, const char* font) { 11 | // if (!CCTextInputNode::init(width, height, caption, thonburi, maxCharCount, font)) 12 | // return false; 13 | 14 | // this->ignoreAnchorPointForPosition(false); 15 | // this->fixPosition(); 16 | 17 | // return true; 18 | // } 19 | 20 | // void fixPosition() { 21 | // if (!m_bIgnoreAnchorPointForPosition && m_placeholderLabel) { 22 | // this->setAnchorPoint(m_placeholderLabel->getAnchorPoint()); 23 | // m_placeholderLabel->setPosition(m_obContentSize * m_obAnchorPoint); 24 | // } 25 | // } 26 | 27 | // void updateLabel(gd::string text) { 28 | // CCTextInputNode::updateLabel(text); 29 | // this->fixPosition(); 30 | // } 31 | // }; 32 | -------------------------------------------------------------------------------- /src/ChallengeNode.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | 8 | using namespace geode::prelude; 9 | using namespace geode::node_ids; 10 | 11 | struct ChallengeNodeIDs : Modify { 12 | struct Fields { 13 | bool m_isNew; 14 | }; 15 | 16 | static void onModify(auto& self) { 17 | if (!self.setHookPriority("ChallengeNode::init", GEODE_ID_PRIORITY)) { 18 | log::warn("Failed to set ChallengeNode::init hook priority, node IDs may not work properly"); 19 | } 20 | } 21 | 22 | bool init(GJChallengeItem* challenge, ChallengesPage* page, bool isNew) { 23 | if(!ChallengeNode::init(challenge, page, isNew)) return false; 24 | 25 | m_fields->m_isNew = isNew; 26 | 27 | NodeIDs::get()->provide(this); 28 | 29 | return true; 30 | } 31 | }; 32 | 33 | $register_ids(ChallengeNode) { 34 | size_t offset = 0; 35 | auto self = reinterpret_cast(this); 36 | 37 | if(!m_challengeItem) { 38 | setIDs( 39 | this, 40 | offset, 41 | "background", 42 | "countdown-label" 43 | ); 44 | 45 | return; 46 | } 47 | 48 | setIDs( 49 | this, 50 | offset, 51 | "background", 52 | "title-label", 53 | "collect-label", 54 | "collect-icon" 55 | ); 56 | 57 | offset += 4; 58 | 59 | if(self->m_fields->m_isNew) { 60 | setIDSafe(this, offset, "new-label"); 61 | offset += 1; 62 | } 63 | 64 | setIDs( 65 | this, 66 | offset, 67 | "progress-bar", 68 | "progress-label" 69 | ); 70 | 71 | offset += 2; 72 | 73 | if(m_challengeItem->m_canClaim) { 74 | if(auto menu = setIDSafe(this, offset, "claim-menu")) { 75 | setIDs( 76 | menu, 77 | 0, 78 | "claim-button" 79 | ); 80 | offset += 1; 81 | } 82 | } 83 | 84 | // technically these get created in the if and else branches 85 | // but they get created in the same order, they just differ in sizes 86 | setIDs( 87 | this, 88 | offset, 89 | "reward-sprite", 90 | "reward-label" 91 | ); 92 | 93 | }; -------------------------------------------------------------------------------- /src/ChallengesPage.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | 8 | using namespace geode::prelude; 9 | using namespace geode::node_ids; 10 | 11 | 12 | $register_ids(ChallengesPage) { 13 | 14 | auto winSize = CCDirector::get()->getWinSize(); 15 | 16 | m_mainLayer->setID("main-layer"); 17 | 18 | setIDSafe(m_mainLayer, 0, "background"); 19 | setIDSafe(m_mainLayer, 0, "loading-circle"); 20 | 21 | setIDs( 22 | m_mainLayer, 23 | 1, 24 | "bottom-left-art", 25 | "top-left-art", 26 | "top-right-art", 27 | "bottom-right-art" 28 | ); 29 | 30 | if(auto topMenu = m_mainLayer->getChildByType(0)) { 31 | setIDs( 32 | topMenu, 33 | 0, 34 | "close-button", 35 | "info-button" 36 | ); 37 | 38 | topMenu->setID("main-menu"); 39 | // leave m_buttonMenu unlayouted, even if it'll have to be empty 40 | // otherwise we could break assumptions from other mods 41 | 42 | auto infoMenu = detachAndCreateMenu( 43 | m_mainLayer, 44 | "top-right-menu", 45 | ColumnLayout::create() 46 | ->setAxisReverse(true) 47 | ->setAxisAlignment(AxisAlignment::End), 48 | topMenu->getChildByID("info-button") 49 | ); 50 | infoMenu->setContentSize({ 60.f, 120.f }); 51 | infoMenu->setPositionY( 52 | infoMenu->getPositionY() - 120.f / 2 + 53 | infoMenu->getChildByID("info-button")->getScaledContentSize().height / 2 54 | ); 55 | infoMenu->updateLayout(); 56 | 57 | auto closeMenu = detachAndCreateMenu( 58 | m_mainLayer, 59 | "top-left-menu", 60 | ColumnLayout::create() 61 | ->setAxisReverse(true) 62 | ->setAxisAlignment(AxisAlignment::End), 63 | topMenu->getChildByID("close-button") 64 | ); 65 | closeMenu->setContentSize({ 60.f, 120.f }); 66 | closeMenu->setPositionY( 67 | closeMenu->getPositionY() - 120.f / 2 + 68 | closeMenu->getChildByID("close-button")->getScaledContentSize().height / 2 69 | ); 70 | closeMenu->updateLayout(); 71 | } 72 | /*m_mainLayer->getChildByType(0)->setID("top-quest"); 73 | m_mainLayer->getChildByType(1)->setID("middle-quest"); 74 | m_mainLayer->getChildByType(2)->setID("bottom-quest");*/ 75 | setIDSafe(m_mainLayer, 0, "new-quest-label"); 76 | 77 | // For some reason this is more reliable? 78 | for(intptr_t i = 0; i <= 2; i++) { 79 | if(!m_dots->objectAtIndex(i)) continue; 80 | auto dot = static_cast(m_dots->objectAtIndex(i)); 81 | 82 | switch(i) { 83 | case 0: dot->setID("top-quest-indicator"); break; 84 | case 1: dot->setID("middle-quest-indicator"); break; 85 | case 2: dot->setID("bottom-quest-indicator"); break; 86 | default: dot->setID(fmt::format("quest-{}-indicator", i)); break; 87 | } 88 | } 89 | 90 | }; 91 | 92 | struct ChallengesPageIDs : Modify { 93 | static void onModify(auto& self) { 94 | if (!self.setHookPriority("ChallengesPage::init", GEODE_ID_PRIORITY)) { 95 | log::warn("Failed to set ChallengesPage::init hook priority, node IDs may not work properly"); 96 | } 97 | if (!self.setHookPriority("ChallengesPage::createChallengeNode", GEODE_ID_PRIORITY)) { 98 | log::warn("Failed to set ChallengesPage::createChallengeNode hook priority, node IDs may not work properly"); 99 | } 100 | } 101 | 102 | ChallengeNode* createChallengeNode(int number, bool skipAnimation, float animLength, bool isNew) { 103 | auto node = ChallengesPage::createChallengeNode(number, skipAnimation, animLength, isNew); 104 | if(!node) return nullptr; 105 | 106 | switch(number) { 107 | case 1: node->setID("top-quest"); break; 108 | case 2: node->setID("middle-quest"); break; 109 | case 3: node->setID("bottom-quest"); break; 110 | default: node->setID(fmt::format("quest-{}", number)); break; 111 | } 112 | 113 | return node; 114 | } 115 | 116 | bool init() { 117 | if(!ChallengesPage::init()) return false; 118 | 119 | NodeIDs::get()->provide(this); 120 | 121 | return true; 122 | } 123 | }; 124 | -------------------------------------------------------------------------------- /src/CharacterColorPage.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | using namespace geode::prelude; 8 | using namespace geode::node_ids; 9 | 10 | $register_ids(CharacterColorPage) { 11 | size_t offset = 0; 12 | 13 | auto colorsLayer = static_cast(this->getChildren()->objectAtIndex(0)); 14 | colorsLayer->setID("colors-layer"); 15 | 16 | setIDSafe(colorsLayer, offset++, "background"); 17 | if (auto menu = colorsLayer->getChildByType(0)) { 18 | menu->setID("buttons-menu"); 19 | setIDs( 20 | menu, 21 | 0, 22 | "close-button", 23 | "ship-button", 24 | "col1-button", 25 | "col2-button", 26 | "glow-button" 27 | ); 28 | 29 | for (int i = 5; i < menu->getChildrenCount()-1; i++) { 30 | auto child = as(menu->getChildren()->objectAtIndex(i)); 31 | child->setID(std::to_string(child->getTag())); 32 | } 33 | 34 | setIDSafe(menu, menu->getChildrenCount()-1, "glow-toggler"); 35 | } 36 | 37 | offset++; 38 | setIDs( 39 | colorsLayer, 40 | offset, 41 | "icon-background", 42 | "cube-icon", 43 | "ball-icon", 44 | "ufo-icon", 45 | "wave-icon", 46 | "robot-icon", 47 | "spider-icon", 48 | "swing-icon", 49 | "cursor-col1", 50 | "cursor-col2", 51 | "cursor-glow", 52 | "glow-label" 53 | ); 54 | } 55 | 56 | struct CharacterColorPageIDS : Modify { 57 | static void onModify(auto& self) { 58 | if (!self.setHookPriority("CharacterColorPage::init", GEODE_ID_PRIORITY)) { 59 | log::warn("Failed to set CharacterColorPage::init hook priority, node IDs may not work properly"); 60 | } 61 | } 62 | 63 | bool init() { 64 | if (!CharacterColorPage::init()) return false; 65 | 66 | NodeIDs::get()->provide(this); 67 | 68 | return true; 69 | } 70 | }; 71 | -------------------------------------------------------------------------------- /src/CommentCell.cpp: -------------------------------------------------------------------------------- 1 | // #include "AddIDs.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | using namespace geode::prelude; 8 | using namespace geode::node_ids; 9 | 10 | $register_ids(CommentCell) { 11 | if(m_comment->m_commentDeleted) return; 12 | 13 | auto winSize = CCDirector::sharedDirector()->getWinSize(); 14 | 15 | bool smallCommentsMode = this->m_height == 36; //this is how robtop does the check 16 | bool usernameNotInMenu = m_comment->m_accountID <= 0 || m_accountComment || m_comment->m_hasLevelID; 17 | 18 | //there is a chance m_comment->m_accountID is not set properly before loadFromComment 19 | //but this is fixed inside loadFromComment itself, so that case is also handled 20 | 21 | if(!smallCommentsMode) m_mainLayer->getChildByType(0)->setID("background"); 22 | 23 | int bmfontOffset = 0; 24 | if(m_comment->m_modBadge > 0) { 25 | m_mainLayer->getChildByType(0)->setID("mod-badge"); 26 | } 27 | if(m_comment->m_percentage > 0) { 28 | m_mainLayer->getChildByType(0 + bmfontOffset)->setID("percentage-label"); 29 | bmfontOffset += 1; 30 | } 31 | if (usernameNotInMenu) { 32 | m_mainLayer->getChildByType(0 + bmfontOffset)->setID("username-label"); 33 | bmfontOffset += 1; 34 | } 35 | 36 | if(smallCommentsMode) { 37 | m_mainLayer->getChildByType(0 + bmfontOffset)->setID("comment-text-label"); 38 | bmfontOffset += 1; 39 | } 40 | 41 | if(!m_comment->m_isSpam) { 42 | m_mainLayer->getChildByType(0 + bmfontOffset)->setID("likes-label"); 43 | bmfontOffset += 1; 44 | if(!(m_comment->m_uploadDate).empty()) m_mainLayer->getChildByType(0 + bmfontOffset)->setID("date-label"); 45 | bmfontOffset += 1; 46 | } 47 | 48 | if (!m_accountComment && usernameNotInMenu) { 49 | m_mainLayer->getChildByType(0)->setID("player-icon"); 50 | } 51 | 52 | if(!smallCommentsMode) m_mainLayer->getChildByType