diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8fc45c5..5107645 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -35,8 +35,8 @@ jobs: - name: Build the mod uses: geode-sdk/build-geode-mod@main with: - bindings: hiimjustin000/bindings - bindings-ref: patch-1 + build-config: RelWithDebInfo + export-pdb: true combine: true target: ${{ matrix.config.target }} diff --git a/CMakeLists.txt b/CMakeLists.txt index 21a5714..5395a60 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,6 +10,7 @@ project(FakeRate VERSION 1.0.0) add_library(${PROJECT_NAME} SHARED src/main.cpp src/FREditPopup.cpp + src/FRUtilities.cpp ) if (NOT DEFINED ENV{GEODE_SDK}) diff --git a/changelog.md b/changelog.md index f8deee7..8705237 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,6 @@ # Fake Rate Changelog +## v1.1.0 (2024-04-26) +- Fixed incompatibility with the mod "More Difficulties" by uproxide + ## v1.0.0 (2024-04-24) - Initial release \ No newline at end of file diff --git a/mod.json b/mod.json index 7ecd26e..c1ed026 100644 --- a/mod.json +++ b/mod.json @@ -5,7 +5,7 @@ "win": "2.204", "mac": "2.200" }, - "version": "v1.0.0", + "version": "v1.1.0", "id": "hiimjustin000.fake_rate", "name": "Fake Rate", "developer": "hiimjustin000", diff --git a/src/FREditPopup.cpp b/src/FREditPopup.cpp index 1753295..802c292 100644 --- a/src/FREditPopup.cpp +++ b/src/FREditPopup.cpp @@ -1,40 +1,10 @@ #include "FREditPopup.hpp" -int FRLevelInfoLayer::getBaseCurrency(int stars) { - switch (stars) { - case 2: - return 40; - case 3: - return 60; - case 4: - return 100; - case 5: - return 140; - case 6: - return 180; - case 7: - return 220; - case 8: - return 280; - case 9: - return 340; - case 10: - return 400; - default: - return 0; - } -} - -int FRLevelInfoLayer::getDifficultyFromLevel(GJGameLevel* level) { - auto difficulty = level->getAverageDifficulty(); - if (level->m_demon > 0) switch (level->m_demonDifficulty) { - case 3: difficulty = 7; break; - case 4: difficulty = 8; break; - case 5: difficulty = 9; break; - case 6: difficulty = 10; break; - default: difficulty = 6; break; - } - return difficulty; +void FRLevelInfoLayer::onModify(auto& self) { + (void)self.setHookPriority("LevelInfoLayer::init", -100); + (void)self.setHookPriority("LevelInfoLayer::levelDownloadFinished", -100); + (void)self.setHookPriority("LevelInfoLayer::levelUpdateFinished", -100); + (void)self.setHookPriority("LevelInfoLayer::likedItem", -100); } bool FRLevelInfoLayer::init(GJGameLevel* level, bool challenge) { @@ -49,11 +19,27 @@ bool FRLevelInfoLayer::init(GJGameLevel* level, bool challenge) { leftSideMenu->addChild(fakeRateButton); leftSideMenu->updateLayout(); + checkFakeRate(); + return true; } -void FRLevelInfoLayer::updateLabelValues() { - LevelInfoLayer::updateLabelValues(); +void FRLevelInfoLayer::levelDownloadFinished(GJGameLevel* level) { + LevelInfoLayer::levelDownloadFinished(level); + checkFakeRate(); +} + +void FRLevelInfoLayer::levelUpdateFinished(GJGameLevel* level, UpdateResponse response) { + LevelInfoLayer::levelUpdateFinished(level, response); + checkFakeRate(); +} + +void FRLevelInfoLayer::likedItem(LikeItemType type, int id, bool liked) { + LevelInfoLayer::likedItem(type, id, liked); + checkFakeRate(); +} + +void FRLevelInfoLayer::checkFakeRate() { auto vec = Mod::get()->getSavedValue>("fake-rate", {}); auto it = std::find_if(vec.begin(), vec.end(), [this](auto const& item) { return item.id == m_level->m_levelID; }); if (it != vec.end()) updateFakeRate(it->stars, it->feature, it->difficulty, false, true); @@ -61,7 +47,7 @@ void FRLevelInfoLayer::updateLabelValues() { .id = m_level->m_levelID, .stars = m_level->m_stars, .feature = m_level->m_featured > 1 ? m_level->m_isEpic + 1 : 0, - .difficulty = getDifficultyFromLevel(m_level) + .difficulty = FRUtilities::getDifficultyFromLevel(m_level) }; } @@ -121,16 +107,17 @@ void FRLevelInfoLayer::updateFakeRate(int stars, int feature, int difficulty, bo if (showStars) { m_orbsIcon->setPositionY(yPos - yOffset * 2.0f); m_orbsLabel->setPositionY(m_orbsIcon->getPositionY()); - auto orbs = FRLevelInfoLayer::getBaseCurrency(stars); + auto orbs = FRUtilities::getBaseCurrency(stars); auto totalOrbs = (int)floorf(orbs * 1.25f); m_orbsLabel->setString(fmt::format("{}/{}", (int)floorf(orbs * m_level->m_orbCompletion / 100.0f), totalOrbs).c_str()); m_orbsLabel->limitLabelWidth(60.0f, 0.5f, 0.0f); } - auto exactTime = static_cast(getChildByID("cvolton.betterinfo/exact-time")); - if (exactTime) { + if (auto exactTime = static_cast(getChildByID("cvolton.betterinfo/exact-time"))) { exactTime->setPositionY(m_lengthLabel->getPositionY() - 2.0f); m_lengthLabel->setPositionY(m_lengthLabel->getPositionY() + 6.0f); } + + if (Loader::get()->isModLoaded("uproxide.more_difficulties")) fixMoreDifficultiesIncompatibility(); } void FRLevelInfoLayer::onFakeRate(CCObject*) { @@ -144,6 +131,41 @@ void FRLevelInfoLayer::onFakeRate(CCObject*) { popup->show(); } +void FRLevelInfoLayer::fixMoreDifficultiesIncompatibility() { + if (auto existingCasualSprite = static_cast(FRUtilities::getChildBySpriteName(this, "uproxide.more_difficulties/MD_Difficulty04.png"))) + existingCasualSprite->removeFromParent(); + if (auto existingToughSprite = static_cast(FRUtilities::getChildBySpriteName(this, "uproxide.more_difficulties/MD_Difficulty07.png"))) + existingToughSprite->removeFromParent(); + if (auto existingCruelSprite = static_cast(FRUtilities::getChildBySpriteName(this, "uproxide.more_difficulties/MD_Difficulty09.png"))) + existingCruelSprite->removeFromParent(); + + m_difficultySprite->setOpacity(255); + auto pos = CCPoint { m_difficultySprite->getPositionX() + 0.25f, m_difficultySprite->getPositionY() - 0.1f }; + switch (m_fields->m_fakeRateData.stars) { + case 4: { + auto casualSprite = CCSprite::create("uproxide.more_difficulties/MD_Difficulty04.png"); + casualSprite->setPosition(pos); + addChild(casualSprite, 3); + m_difficultySprite->setOpacity(0); + break; + } + case 7: { + auto toughSprite = CCSprite::create("uproxide.more_difficulties/MD_Difficulty07.png"); + toughSprite->setPosition(pos); + addChild(toughSprite, 3); + m_difficultySprite->setOpacity(0); + break; + } + case 9: { + auto cruelSprite = CCSprite::create("uproxide.more_difficulties/MD_Difficulty09.png"); + cruelSprite->setPosition(pos); + addChild(cruelSprite, 3); + m_difficultySprite->setOpacity(0); + break; + } + } +} + FREditPopup* FREditPopup::create(GJGameLevel* level, int stars, int feature, int difficulty) { auto ret = new FREditPopup(); if (ret && ret->initAnchored(200.0f, 150.0f, level, stars, feature, difficulty)) { @@ -154,20 +176,6 @@ FREditPopup* FREditPopup::create(GJGameLevel* level, int stars, int feature, int return nullptr; } -int FREditPopup::getDifficultyForStars(int stars) { - switch (stars) { - case 0: return 0; - case 1: return -1; - case 2: return 1; - case 3: return 2; - case 4: case 5: return 3; - case 6: case 7: return 4; - case 8: case 9: return 5; - case 10: return 6; - default: return 0; - } -} - bool FREditPopup::setup(GJGameLevel* level, int stars, int feature, int difficulty) { setTitle("Fake Rate"); m_level = level; @@ -179,6 +187,25 @@ bool FREditPopup::setup(GJGameLevel* level, int stars, int feature, int difficul m_difficultySprite->setPositionX(100.0f); m_mainLayer->addChild(m_difficultySprite); + if (Loader::get()->isModInstalled("uproxide.more_difficulties")) { + auto pos = CCPoint { 100.25f, 84.9f }; + + m_casualSprite = CCSprite::create("uproxide.more_difficulties/MD_Difficulty04.png"); + m_casualSprite->setPosition(pos); + m_casualSprite->setVisible(false); + m_mainLayer->addChild(m_casualSprite); + + m_toughSprite = CCSprite::create("uproxide.more_difficulties/MD_Difficulty07.png"); + m_toughSprite->setPosition(pos); + m_toughSprite->setVisible(false); + m_mainLayer->addChild(m_toughSprite); + + m_cruelSprite = CCSprite::create("uproxide.more_difficulties/MD_Difficulty09.png"); + m_cruelSprite->setPosition(pos); + m_cruelSprite->setVisible(false); + m_mainLayer->addChild(m_cruelSprite); + } + m_starSprite = CCSprite::createWithSpriteFrameName(m_level->m_levelLength < 5 ? "star_small01_001.png" : "moon_small01_001.png"); m_mainLayer->addChild(m_starSprite); @@ -286,7 +313,7 @@ void FREditPopup::onFeatureRight(CCObject*) { } void FREditPopup::updateLabels() { - m_difficulty = m_stars >= 10 ? m_difficulty > 5 ? m_difficulty : 7 : getDifficultyForStars(m_stars); + m_difficulty = m_stars >= 10 ? m_difficulty > 5 ? m_difficulty : 7 : FRUtilities::getDifficultyForStars(m_stars); m_difficultySprite->updateDifficultyFrame(m_difficulty, (GJDifficultyName)1); m_difficultySprite->updateFeatureState((GJFeatureState)m_feature); m_difficultySprite->setPositionY(85.0f + (m_difficulty > 5 ? 5.0f : 0.0f)); @@ -301,6 +328,13 @@ void FREditPopup::updateLabels() { m_difficultyRightArrow->setEnabled(m_difficulty > 5); m_featureLeftArrow->setPositionY(m_difficultySprite->getPositionY() + 5.0f); m_featureRightArrow->setPositionY(m_difficultySprite->getPositionY() + 5.0f); + if (Loader::get()->isModInstalled("uproxide.more_difficulties")) { + m_casualSprite->setVisible(m_stars == 4); + m_toughSprite->setVisible(m_stars == 7); + m_cruelSprite->setVisible(m_stars == 9); + if (m_stars == 4 || m_stars == 7 || m_stars == 9) m_difficultySprite->setOpacity(0); + else m_difficultySprite->setOpacity(255); + } } void FREditPopup::onAdd(CCObject*) { diff --git a/src/FREditPopup.hpp b/src/FREditPopup.hpp index 08420bc..8e1b083 100644 --- a/src/FREditPopup.hpp +++ b/src/FREditPopup.hpp @@ -1,8 +1,6 @@ #include -#include #include - -using namespace geode::prelude; +#include "FRUtilities.hpp" struct FakeRateSaveData { int id; @@ -11,16 +9,19 @@ struct FakeRateSaveData { int difficulty; }; -class FRLevelInfoLayerDummy; struct FRLevelInfoLayer : geode::Modify { +class FRLevelInfoLayerDummy; struct FRLevelInfoLayer : Modify { struct Fields { FakeRateSaveData m_fakeRateData; }; - static int getDifficultyFromLevel(GJGameLevel*); - static int getBaseCurrency(int); + static void onModify(auto& self); bool init(GJGameLevel*, bool); - void updateLabelValues(); + void levelDownloadFinished(GJGameLevel*); + void levelUpdateFinished(GJGameLevel*, UpdateResponse); + void likedItem(LikeItemType, int, bool); + void checkFakeRate(); void updateFakeRate(int, int, int, bool, bool); void onFakeRate(CCObject*); + void fixMoreDifficultiesIncompatibility(); }; class FREditPopup : public Popup { @@ -30,6 +31,9 @@ class FREditPopup : public Popup { int m_feature; int m_difficulty; GJDifficultySprite* m_difficultySprite; + CCSprite* m_casualSprite; + CCSprite* m_toughSprite; + CCSprite* m_cruelSprite; CCSprite* m_starSprite; CCLabelBMFont* m_starsLabel; CCMenuItemSpriteExtra* m_starLeftArrow; @@ -44,7 +48,6 @@ class FREditPopup : public Popup { public: FRLevelInfoLayer* m_delegate; static FREditPopup* create(GJGameLevel*, int, int, int); - static int getDifficultyForStars(int); void onStarLeft(CCObject*); void onStarRight(CCObject*); diff --git a/src/FRUtilities.cpp b/src/FRUtilities.cpp new file mode 100644 index 0000000..b429240 --- /dev/null +++ b/src/FRUtilities.cpp @@ -0,0 +1,67 @@ +#include "FRUtilities.hpp" + +int FRUtilities::getBaseCurrency(int stars) { + switch (stars) { + case 2: return 40; + case 3: return 60; + case 4: return 100; + case 5: return 140; + case 6: return 180; + case 7: return 220; + case 8: return 280; + case 9: return 340; + case 10: return 400; + default: return 0; + } +} + +CCNode* FRUtilities::getChildBySpriteName(CCNode* parent, const char* name) { + for (auto child : CCArrayExt(parent->getChildren())) { + if (isSpriteName(static_cast(child), name)) return child; + } + return nullptr; +} + +int FRUtilities::getDifficultyForStars(int stars) { + switch (stars) { + case 0: return 0; + case 1: return -1; + case 2: return 1; + case 3: return 2; + case 4: case 5: return 3; + case 6: case 7: return 4; + case 8: case 9: return 5; + case 10: return 6; + default: return 0; + } +} + +int FRUtilities::getDifficultyFromLevel(GJGameLevel* level) { + auto difficulty = level->getAverageDifficulty(); + if (level->m_demon > 0) switch (level->m_demonDifficulty) { + case 3: difficulty = 7; break; + case 4: difficulty = 8; break; + case 5: difficulty = 9; break; + case 6: difficulty = 10; break; + default: difficulty = 6; break; + } + return difficulty; +} + +bool FRUtilities::isSpriteName(CCNode* node, const char* name) { + if (!node) return false; + + auto texture = CCTextureCache::sharedTextureCache()->textureForKey(name); + if (!texture) return false; + + if (auto* spr = typeinfo_cast(node)) { + if (spr->getTexture() == texture) return true; + } + else if (auto* btn = typeinfo_cast(node)) { + auto* img = btn->getNormalImage(); + if (auto* spr = typeinfo_cast(img)) { + if (spr->getTexture() == texture) return true; + } + } + return false; +} \ No newline at end of file diff --git a/src/FRUtilities.hpp b/src/FRUtilities.hpp new file mode 100644 index 0000000..5f88bd7 --- /dev/null +++ b/src/FRUtilities.hpp @@ -0,0 +1,12 @@ +#include + +using namespace geode::prelude; + +class FRUtilities { +public: + static int getBaseCurrency(int); + static CCNode* getChildBySpriteName(CCNode*, const char*); + static int getDifficultyForStars(int); + static int getDifficultyFromLevel(GJGameLevel*); + static bool isSpriteName(CCNode*, const char*); +}; \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 978e14d..43fc64f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2,6 +2,10 @@ #include "FREditPopup.hpp" class $modify(FRLevelCell, LevelCell) { + static void onModify(auto& self) { + (void)self.setHookPriority("LevelCell::loadFromLevel", -100); + } + void loadFromLevel(GJGameLevel* level) { LevelCell::loadFromLevel(level); auto difficultyContainer = m_mainLayer->getChildByID("difficulty-container"); @@ -36,16 +40,13 @@ class $modify(FRLevelCell, LevelCell) { starsLabel->setString(std::to_string(fakeRateData.stars).c_str()); } else { - auto starsIcon = static_cast(difficultyContainer->getChildByID("stars-icon")); - if (starsIcon) starsIcon->removeFromParent(); - auto starsLabel = static_cast(difficultyContainer->getChildByID("stars-label")); - if (starsLabel) starsLabel->removeFromParent(); + if (auto starsIcon = static_cast(difficultyContainer->getChildByID("stars-icon"))) starsIcon->removeFromParent(); + if (auto starsLabel = static_cast(difficultyContainer->getChildByID("stars-label"))) starsLabel->removeFromParent(); } auto gsm = GameStatsManager::sharedState(); auto coinParent = m_compactView ? m_mainLayer : difficultyContainer; for (int i = 1; i <= 3; i++) { - auto coin = static_cast(coinParent->getChildByID("coin-icon-" + std::to_string(i))); - if (coin) { + if (auto coin = static_cast(coinParent->getChildByID("coin-icon-" + std::to_string(i)))) { if (!m_compactView) coin->setPositionY(difficultySprite->getPositionY() - 31.5f - (showStars ? 14.0f : 0.0f) - (m_level->m_gauntletLevel || m_level->m_dailyID > 0 ? 14.5f : 0.0f)); auto coinStr = fmt::format("{}_{}", m_level->m_levelID.value(), i); @@ -93,7 +94,7 @@ class $modify(FRLevelCell, LevelCell) { orbsLabel->setID("orbs-label"); m_mainLayer->addChild(orbsLabel); } - auto orbs = FRLevelInfoLayer::getBaseCurrency(fakeRateData.stars); + auto orbs = FRUtilities::getBaseCurrency(fakeRateData.stars); auto totalOrbs = (int)floorf(orbs * 1.25f); orbsLabel->setString((m_level->m_orbCompletion == 100 ? fmt::format("{}", totalOrbs) : @@ -102,11 +103,49 @@ class $modify(FRLevelCell, LevelCell) { if (m_level->m_orbCompletion == 100) orbsLabel->setColor({ 100, 255, 255 }); } else { - auto orbsIcon = static_cast(m_mainLayer->getChildByID("orbs-icon")); - if (orbsIcon) orbsIcon->removeFromParent(); - auto orbsLabel = static_cast(m_mainLayer->getChildByID("orbs-label")); - if (orbsLabel) orbsLabel->removeFromParent(); + if (auto orbsIcon = static_cast(m_mainLayer->getChildByID("orbs-icon"))) orbsIcon->removeFromParent(); + if (auto orbsLabel = static_cast(m_mainLayer->getChildByID("orbs-label"))) orbsLabel->removeFromParent(); } + + if (Loader::get()->isModLoaded("uproxide.more_difficulties")) fixMoreDifficultiesIncompatibility(fakeRateData); + } + } + } + + void fixMoreDifficultiesIncompatibility(FakeRateSaveData const& fakeRateData) { + auto difficultyContainer = m_mainLayer->getChildByID("difficulty-container"); + + if (auto existingCasualSprite = static_cast(FRUtilities::getChildBySpriteName(difficultyContainer, "uproxide.more_difficulties/MD_Difficulty04.png"))) + existingCasualSprite->removeFromParent(); + if (auto existingToughSprite = static_cast(FRUtilities::getChildBySpriteName(difficultyContainer, "uproxide.more_difficulties/MD_Difficulty07.png"))) + existingToughSprite->removeFromParent(); + if (auto existingCruelSprite = static_cast(FRUtilities::getChildBySpriteName(difficultyContainer, "uproxide.more_difficulties/MD_Difficulty09.png"))) + existingCruelSprite->removeFromParent(); + + auto difficultySprite = static_cast(difficultyContainer->getChildByID("difficulty-sprite")); + difficultySprite->setOpacity(255); + auto pos = CCPoint { difficultySprite->getPositionX() + 0.25f, difficultySprite->getPositionY() - 0.1f }; + switch (fakeRateData.stars) { + case 4: { + auto casualSprite = CCSprite::create("uproxide.more_difficulties/MD_Difficulty04.png"); + casualSprite->setPosition(pos); + difficultyContainer->addChild(casualSprite, 3); + difficultySprite->setOpacity(0); + break; + } + case 7: { + auto toughSprite = CCSprite::create("uproxide.more_difficulties/MD_Difficulty07.png"); + toughSprite->setPosition(pos); + difficultyContainer->addChild(toughSprite, 3); + difficultySprite->setOpacity(0); + break; + } + case 9: { + auto cruelSprite = CCSprite::create("uproxide.more_difficulties/MD_Difficulty09.png"); + cruelSprite->setPosition(pos); + difficultyContainer->addChild(cruelSprite, 3); + difficultySprite->setOpacity(0); + break; } } }