From 2b7f74b959a9988dc55c4874cf2fc224a4788f5d Mon Sep 17 00:00:00 2001 From: Justin Pridgen Date: Thu, 18 Jul 2024 06:07:09 -0400 Subject: [PATCH] v1.1.5 --- CMakeLists.txt | 2 +- README.md | 1 + about.md | 1 + changelog.md | 3 +++ mod.json | 2 +- src/DemonsInBetween.cpp | 55 +++++++++++++++++++++++++++++++++++------ src/DemonsInBetween.hpp | 4 +++ src/main.cpp | 43 ++++++++++++++++++++++++++++---- 8 files changed, 97 insertions(+), 14 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e1724cb..f1deac3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_OSX_ARCHITECTURES "x86_64;arm64") set(CMAKE_CXX_VISIBILITY_PRESET hidden) -project(DemonsInBetween VERSION 1.1.4) +project(DemonsInBetween VERSION 1.1.5) add_library(${PROJECT_NAME} SHARED src/DemonsInBetween.cpp diff --git a/README.md b/README.md index be749cd..5beb360 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ A mod that adds additional demon difficulties in between existing ones. # Features - Fifteen new demon difficulties in between the five existing ones, determined using the [Geometry Dash Demon Ladder](https://gdladder.com) - A quick search popup in the level search menu for the new demon difficulties +- Difficulty reloading in the level info page, using the refresh button # Credits - [SufficientEbb](https://gdbrowser.com/u/20865884) - Idea for the mod diff --git a/about.md b/about.md index d89cfa0..1453cec 100644 --- a/about.md +++ b/about.md @@ -4,6 +4,7 @@ A mod that adds additional demon difficulties in between existing ones. # Features - Fifteen new demon difficulties in between the five existing ones, determined using the [Geometry Dash Demon Ladder](https://gdladder.com) - A quick search popup in the level search menu for the new demon difficulties +- Difficulty reloading in the level info page, using the refresh button # Credits - [SufficientEbb](user:20865884) - Idea for the mod diff --git a/changelog.md b/changelog.md index ea43798..b250133 100644 --- a/changelog.md +++ b/changelog.md @@ -1,4 +1,7 @@ # Demons In Between Changelog +## v1.1.5 (2024-07-18) +- Added cache refreshing for a level's demon difficulty under the refresh button in the level info page + ## v1.1.4 (2024-07-11) - Added some compatibility with the mod "GodlikeFaces" by adyagmd diff --git a/mod.json b/mod.json index 9922725..3be1660 100644 --- a/mod.json +++ b/mod.json @@ -5,7 +5,7 @@ "win": "2.206", "mac": "2.206" }, - "version": "v1.1.4", + "version": "v1.1.5", "id": "hiimjustin000.demons_in_between", "name": "Demons In Between", "developer": "hiimjustin000", diff --git a/src/DemonsInBetween.cpp b/src/DemonsInBetween.cpp index b45e24b..d65094f 100644 --- a/src/DemonsInBetween.cpp +++ b/src/DemonsInBetween.cpp @@ -16,7 +16,7 @@ void DemonsInBetween::tryLoadCache() { loadGDDL(); return; } - + std::stringstream bufferStream; bufferStream << file.rdbuf(); @@ -27,7 +27,7 @@ void DemonsInBetween::tryLoadCache() { loadGDDL(); return; } - + auto cache = json.value(); if (!cache.contains("cached") || !cache["cached"].is_number() || !cache.contains("list") || !cache["list"].is_array()) { log::error("GDDL cache is corrupted, loading from spreadsheet"); @@ -42,6 +42,7 @@ void DemonsInBetween::tryLoadCache() { return; } + GDDL_CACHE = cache; initGDDL(cache["list"].as_array()); } @@ -64,10 +65,8 @@ void DemonsInBetween::loadGDDL() { void DemonsInBetween::initGDDL(matjson::Array const& gddl, bool saveCache) { if (saveCache) { - auto file = std::fstream(CACHE_PATH, std::ios::out); - file << matjson::Value(matjson::Object({ { "cached", time(0) }, { "list", gddl } })).dump(0); - file.close(); - log::info("Successfully saved GDDL cache"); + GDDL_CACHE = matjson::Object({ { "cached", time(0) }, { "list", gddl } }); + saveGDDL(); } for (auto const& demon : gddl) { @@ -76,6 +75,13 @@ void DemonsInBetween::initGDDL(matjson::Array const& gddl, bool saveCache) { } } +void DemonsInBetween::saveGDDL() { + auto file = std::fstream(CACHE_PATH, std::ios::out); + file << GDDL_CACHE.dump(0); + file.close(); + log::info("Successfully saved GDDL cache"); +} + matjson::Array const& DemonsInBetween::parseGDDL(std::string const& data) { // MAKE SURE WE ARE NOT SPLITTING THE COMMAS IN THE QUOTATION MARKS auto lines = string::split(data, "\n"); @@ -88,7 +94,7 @@ matjson::Array const& DemonsInBetween::parseGDDL(std::string const& data) { auto key = keys[j]; auto value = values[j]; if (key == "ID") demon[key] = std::stoi(value); - else if (key == "Tier" || key == "Enjoyment") demon[key] = value != "" ? std::stod(value) : matjson::Value(nullptr); + else if (key == "Tier" || key == "Enjoyment") demon[key] = value != "" ? round(std::stod(value) * 100) / 100 : matjson::Value(nullptr); else demon[key] = value; } demons.push_back(demon); @@ -105,6 +111,41 @@ LadderDemon const& DemonsInBetween::demonForLevel(GJGameLevel* level) { return demon == GDDL.end() ? defaultDemon : *demon; } +void DemonsInBetween::refreshDemonForLevel(EventListener&& listenerRef, GJGameLevel* level, MiniFunction callback) { + auto levelID = level->m_levelID.value(); + auto demon = std::find_if(GDDL.begin(), GDDL.end(), [levelID](auto const& d) { + return d.id == levelID; + }); + if (demon == GDDL.end()) return; + + auto&& listener = std::move(listenerRef); + listener.bind([callback, demon](web::WebTask::Event* e) { + if (auto res = e->getValue()) { + if (res->ok()) { + auto json = res->json().value(); + if (json["Rating"].is_null()) return; + + demon->difficulty = DIFFICULTY_INDICES[(int)round(json["Rating"].as_double())]; + + auto& cacheArray = GDDL_CACHE["list"].as_array(); + auto cacheDemon = std::find_if(cacheArray.begin(), cacheArray.end(), [demon](matjson::Value const& d) { + return d["ID"].as_int() == demon->id; + }); + if (cacheDemon != cacheArray.end()) { + cacheDemon->operator[]("Tier") = round(json["Rating"].as_double() * 100) / 100; + cacheDemon->operator[]("Enjoyment") = !json["Enjoyment"].is_null() ? round(json["Enjoyment"].as_double() * 100) / 100 : matjson::Value(nullptr); + GDDL_CACHE_CHANGED = true; + log::info("Updated demon with ID {}", demon->id); + } else log::error("Failed to update demon with ID {}", demon->id); + + if (callback) callback(*demon); + } + } + }); + + listener.setFilter(web::WebRequest().get(fmt::format("https://gdladder.com/api/level/{}", demon->id).c_str())); +} + CCSprite* DemonsInBetween::spriteForDifficulty(GJDifficultySprite* difficultySprite, int difficulty, GJDifficultyName name, GJFeatureState state) { auto glow = state == GJFeatureState::Legendary ? "_4" : ""; auto sprite = CCSprite::createWithSpriteFrameName((name == GJDifficultyName::Long ? diff --git a/src/DemonsInBetween.hpp b/src/DemonsInBetween.hpp index 9f3eca1..be200f1 100644 --- a/src/DemonsInBetween.hpp +++ b/src/DemonsInBetween.hpp @@ -28,6 +28,8 @@ class DemonsInBetween { }; public: inline static std::vector GDDL = {}; + inline static matjson::Value GDDL_CACHE = {}; + inline static bool GDDL_CACHE_CHANGED = false; inline static bool TRIED_LOADING = false; inline static int MAX_PAGE = 0; inline static int DIFFICULTY = 0; @@ -38,8 +40,10 @@ class DemonsInBetween { static void tryLoadCache(); static void loadGDDL(); static void initGDDL(matjson::Array const&, bool saveCache = false); + static void saveGDDL(); static matjson::Array const& parseGDDL(std::string const&); static LadderDemon const& demonForLevel(GJGameLevel*); + static void refreshDemonForLevel(EventListener&&, GJGameLevel*, MiniFunction); static CCSprite* spriteForDifficulty(GJDifficultySprite*, int, GJDifficultyName, GJFeatureState); static GJFeatureState stateForLevel(GJGameLevel*); static GJSearchObject* searchObjectForPage(int); diff --git a/src/main.cpp b/src/main.cpp index 27212dd..e711b5b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,5 +1,9 @@ #include "DIBSearchPopup.hpp" +$on_mod(DataSaved) { + if (DemonsInBetween::GDDL_CACHE_CHANGED) DemonsInBetween::saveGDDL(); +} + #include class $modify(DIBMenuLayer, MenuLayer) { bool init() { @@ -44,24 +48,53 @@ class $modify(DIBLevelSearchLayer, LevelSearchLayer) { #include class $modify(DIBLevelInfoLayer, LevelInfoLayer) { + struct Fields { + EventListener m_listener; + bool m_disabled; + }; + static void onModify(auto& self) { - (void)self.setHookPriority("LevelInfoLayer::init", -50); + (void)self.setHookPriority("LevelInfoLayer::init", -50); // gddp integration is -42 D: } bool init(GJGameLevel* level, bool challenge) { if (!LevelInfoLayer::init(level, challenge)) return false; - if (getChildByID("grd-difficulty") || getChildByID("gddp-difficulty")) return true; + if (getChildByID("grd-difficulty") || getChildByID("gddp-difficulty")) { + m_fields->m_disabled = true; + return true; + } auto& demon = DemonsInBetween::demonForLevel(level); if (demon.id == 0) return true; - addChild(DemonsInBetween::spriteForDifficulty(m_difficultySprite, demon.difficulty, - GJDifficultyName::Long, DemonsInBetween::stateForLevel(level)), 3); - m_difficultySprite->setOpacity(0); + createDemonSprite(demon); return true; } + + void createDemonSprite(LadderDemon const& demon) { + if (m_fields->m_disabled) return; + + if (auto existingDifficulty = getChildByID("between-difficulty-sprite"_spr)) existingDifficulty->removeFromParentAndCleanup(true); + addChild(DemonsInBetween::spriteForDifficulty(m_difficultySprite, demon.difficulty, GJDifficultyName::Long, DemonsInBetween::stateForLevel(m_level)), 3); + m_difficultySprite->setOpacity(0); + } + + void onUpdate(CCObject* sender) { + LevelInfoLayer::onUpdate(sender); + if (m_fields->m_disabled) return; + + if (!m_isBusy && GameLevelManager::sharedState()->isTimeValid(std::to_string(m_level->m_levelID.value()).c_str(), 3600.0f)) + DemonsInBetween::refreshDemonForLevel(std::move(m_fields->m_listener), m_level, [this](LadderDemon const& demon) { createDemonSprite(demon); }); + } + + void levelUpdateFinished(GJGameLevel* level, UpdateResponse response) override { + LevelInfoLayer::levelUpdateFinished(level, response); + if (m_fields->m_disabled) return; + + DemonsInBetween::refreshDemonForLevel(std::move(m_fields->m_listener), level, [this](LadderDemon const& demon) { createDemonSprite(demon); }); + } }; #include