From ef2b4e17189481fd711eb612099283e1d2d62044 Mon Sep 17 00:00:00 2001 From: Justin Pridgen Date: Fri, 1 Nov 2024 13:53:48 -0400 Subject: [PATCH] RIP commit histories --- CMakeLists.txt | 12 +- changelog.md | 3 + mod.json | 4 +- src/IntegratedDemonlist.cpp | 200 ++++++++++++------------------ src/IntegratedDemonlist.hpp | 34 +++-- src/{ => classes}/IDListLayer.cpp | 26 ++-- src/{ => classes}/IDListLayer.hpp | 30 ++--- src/{ => classes}/IDPackCell.cpp | 2 + src/{ => classes}/IDPackCell.hpp | 4 +- src/{ => classes}/IDPackLayer.cpp | 8 +- src/{ => classes}/IDPackLayer.hpp | 22 ++-- src/hooks/LevelBrowserLayer.cpp | 28 +++++ src/hooks/LevelCell.cpp | 97 +++++++++++++++ src/hooks/LevelSearchLayer.cpp | 23 ++++ src/main.cpp | 115 ----------------- 15 files changed, 313 insertions(+), 295 deletions(-) rename src/{ => classes}/IDListLayer.cpp (95%) rename src/{ => classes}/IDListLayer.hpp (68%) rename src/{ => classes}/IDPackCell.cpp (99%) rename src/{ => classes}/IDPackCell.hpp (53%) rename src/{ => classes}/IDPackLayer.cpp (99%) rename src/{ => classes}/IDPackLayer.hpp (68%) create mode 100644 src/hooks/LevelBrowserLayer.cpp create mode 100644 src/hooks/LevelCell.cpp create mode 100644 src/hooks/LevelSearchLayer.cpp delete mode 100644 src/main.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 2f695d9..9a3d8d6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,14 +4,16 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_OSX_ARCHITECTURES "x86_64;arm64") set(CMAKE_CXX_VISIBILITY_PRESET hidden) -project(IntegratedDemonlist VERSION 1.6.5) +project(IntegratedDemonlist VERSION 1.7.0) add_library(${PROJECT_NAME} SHARED - src/IDListLayer.cpp - src/IDPackCell.cpp - src/IDPackLayer.cpp + src/classes/IDListLayer.cpp + src/classes/IDPackCell.cpp + src/classes/IDPackLayer.cpp + src/hooks/LevelBrowserLayer.cpp + src/hooks/LevelCell.cpp + src/hooks/LevelSearchLayer.cpp src/IntegratedDemonlist.cpp - src/main.cpp ) if (NOT DEFINED ENV{GEODE_SDK}) diff --git a/changelog.md b/changelog.md index 0b7f8c1..cbf97eb 100644 --- a/changelog.md +++ b/changelog.md @@ -1,4 +1,7 @@ # Integrated Demonlist Changelog +## v1.7.0 (2024-11-01) +- Changed ranking text to load individually on cell load rather than all at once in the main menu + ## v1.6.5 (2024-10-25) - Fixed two-player demons not appearing in the demonlist twice (Someone wanted this for some reason) - Permanently fixed the Pemonlist issue (New API) diff --git a/mod.json b/mod.json index 54d64dd..1ebd530 100644 --- a/mod.json +++ b/mod.json @@ -1,11 +1,11 @@ { - "geode": "3.8.1", + "geode": "3.9.0", "gd": { "android": "2.206", "win": "2.206", "mac": "2.206" }, - "version": "v1.6.5", + "version": "v1.7.0", "id": "hiimjustin000.integrated_demonlist", "name": "Integrated Demonlist", "developer": "hiimjustin000", diff --git a/src/IntegratedDemonlist.cpp b/src/IntegratedDemonlist.cpp index d1fc228..1cdd5f6 100644 --- a/src/IntegratedDemonlist.cpp +++ b/src/IntegratedDemonlist.cpp @@ -1,50 +1,13 @@ #include "IntegratedDemonlist.hpp" +using namespace geode::prelude; + #define AREDL_URL "https://api.aredl.net/api/aredl/levels" #define AREDL_PACKS_URL "https://api.aredl.net/api/aredl/packs" #define PEMONLIST_UPTIME_URL "https://pemonlist.com/api/uptime?version=2" #define PEMONLIST_URL "https://pemonlist.com/api/list?limit=500&version=2" -void IntegratedDemonlist::initializeAREDL(web::WebResponse* res) { - AREDL.clear(); - auto str = res->string().value(); - std::string error; - auto json = matjson::parse(str, error).value_or(matjson::Array()); - if (!error.empty()) log::error("Failed to parse AREDL: {}", error); - if (json.is_array()) for (auto const& level : json.as_array()) { - if (level.contains("legacy") && level["legacy"].is_bool() && level["legacy"].as_bool()) continue; - if (!level.contains("level_id") || !level["level_id"].is_number()) continue; - if (!level.contains("name") || !level["name"].is_string()) continue; - if (!level.contains("position") || !level["position"].is_number()) continue; - - AREDL.push_back({ - level["level_id"].as_int(), - level["name"].as_string(), - level["position"].as_int() - }); - } -} - -void IntegratedDemonlist::initializePemonlist(web::WebResponse* res) { - PEMONLIST.clear(); - auto str = res->string().value(); - std::string error; - auto json = matjson::parse(str, error).value_or(matjson::Object { { "data", matjson::Array() } }); - if (!error.empty()) log::error("Failed to parse Pemonlist: {}", error); - if (json.is_object() && json.contains("data") && json["data"].is_array()) for (auto const& level : json["data"].as_array()) { - if (!level.contains("level_id") || !level["level_id"].is_number()) continue; - if (!level.contains("name") || !level["name"].is_string()) continue; - if (!level.contains("placement") || !level["placement"].is_number()) continue; - - PEMONLIST.push_back({ - level["level_id"].as_int(), - level["name"].as_string(), - level["placement"].as_int() - }); - } -} - -void IntegratedDemonlist::isOk(std::string const& url, EventListener&& listenerRef, utils::MiniFunction callback) { +void IntegratedDemonlist::isOk(std::string const& url, EventListener&& listenerRef, std::function const& callback) { auto&& listener = std::move(listenerRef); listener.bind([callback](web::WebTask::Event* e) { if (auto res = e->getValue()) callback(res->ok(), res->code()); @@ -52,45 +15,45 @@ void IntegratedDemonlist::isOk(std::string const& url, EventListener task = std::nullopt; - static std::optional okTask = std::nullopt; - okTask = web::WebRequest().downloadRange({ 0, 0 }).get(AREDL_URL).map([](web::WebResponse* res) { - if (!res->ok()) { - log::error("Failed to load AREDL with status code {}", res->code()); - okTask = std::nullopt; - return *res; - } - - task = web::WebRequest().get(AREDL_URL).map([](web::WebResponse* res2) { - if (res2->ok()) initializeAREDL(res2); - else log::error("Failed to load AREDL with status code {}", res2->code()); - - task = std::nullopt; - return *res2; - }); - - okTask = std::nullopt; - return *res; - }); -} - void IntegratedDemonlist::loadAREDL( EventListener&& listenerRef, EventListener&& okListener, - LoadingCircle* circle, utils::MiniFunction callback + LoadingCircle* circle, std::function const& callback ) { auto&& listener = std::move(listenerRef); - listener.bind([callback](web::WebTask::Event* e) { + listener.bind([callback, circle](web::WebTask::Event* e) { if (auto res = e->getValue()) { - if (res->ok()) { - initializeAREDL(res); - callback(); + if (!res->ok()) { + queueInMainThread([circle, res] { + FLAlertLayer::create(fmt::format("Load Failed ({})", res->code()).c_str(), "Failed to load AREDL. Please try again later.", "OK")->show(); + circle->setVisible(false); + }); + return; + } + + AREDL_LOADED = true; + AREDL.clear(); + auto str = res->string().value(); + std::string error; + auto json = matjson::parse(str, error).value_or(matjson::Array()); + if (!error.empty()) log::error("Failed to parse AREDL: {}", error); + if (json.is_array()) for (auto const& level : json.as_array()) { + if (level.contains("legacy") && level["legacy"].is_bool() && level["legacy"].as_bool()) continue; + if (!level.contains("level_id") || !level["level_id"].is_number()) continue; + if (!level.contains("name") || !level["name"].is_string()) continue; + if (!level.contains("position") || !level["position"].is_number()) continue; + + AREDL.push_back({ + level["level_id"].as_int(), + level["name"].as_string(), + level["position"].as_int() + }); } + callback(); } }); isOk(AREDL_URL, std::move(okListener), [&listener, circle](bool ok, int code) { if (ok) listener.setFilter(web::WebRequest().get(AREDL_URL)); - else Loader::get()->queueInMainThread([circle, code] { + else queueInMainThread([circle, code] { FLAlertLayer::create(fmt::format("Load Failed ({})", code).c_str(), "Failed to load AREDL. Please try again later.", "OK")->show(); circle->setVisible(false); }); @@ -99,82 +62,83 @@ void IntegratedDemonlist::loadAREDL( void IntegratedDemonlist::loadAREDLPacks( EventListener&& listenerRef, EventListener&& okListener, - LoadingCircle* circle, utils::MiniFunction callback + LoadingCircle* circle, std::function const& callback ) { auto&& listener = std::move(listenerRef); - listener.bind([callback](web::WebTask::Event* e) { + listener.bind([callback, circle](web::WebTask::Event* e) { if (auto res = e->getValue()) { - if (res->ok()) { - AREDL_PACKS.clear(); - for (auto const& pack : res->json().value().as_array()) { - std::vector levels; - for (auto const& level : pack["levels"].as_array()) levels.push_back(level["level_id"].as_int()); - AREDL_PACKS.push_back({ - pack["name"].as_string(), - pack["points"].as_double(), - levels - }); - } - std::sort(AREDL_PACKS.begin(), AREDL_PACKS.end(), [](auto const& a, auto const& b) { - return a.points < b.points; + if (!res->ok()) { + queueInMainThread([circle, res] { + FLAlertLayer::create(fmt::format("Load Failed ({})", res->code()).c_str(), "Failed to load AREDL packs. Please try again later.", "OK")->show(); + circle->setVisible(false); }); - callback(); + return; } + + AREDL_PACKS.clear(); + for (auto const& pack : res->json().value().as_array()) { + std::vector levels; + for (auto const& level : pack["levels"].as_array()) levels.push_back(level["level_id"].as_int()); + AREDL_PACKS.push_back({ + pack["name"].as_string(), + pack["points"].as_double(), + levels + }); + } + std::sort(AREDL_PACKS.begin(), AREDL_PACKS.end(), [](auto const& a, auto const& b) { + return a.points < b.points; + }); + callback(); } }); isOk(AREDL_PACKS_URL, std::move(okListener), [&listener, circle](bool ok, int code) { if (ok) listener.setFilter(web::WebRequest().get(AREDL_PACKS_URL)); - else Loader::get()->queueInMainThread([circle, code] { + else queueInMainThread([circle, code] { FLAlertLayer::create(fmt::format("Load Failed ({})", code).c_str(), "Failed to load AREDL packs. Please try again later.", "OK")->show(); circle->setVisible(false); }); }); } -void IntegratedDemonlist::loadPemonlist() { - static std::optional task = std::nullopt; - static std::optional okTask = std::nullopt; - okTask = web::WebRequest().downloadRange({ 0, 0 }).get(PEMONLIST_UPTIME_URL).map([](web::WebResponse* res) { - if (!res->ok()) { - log::error("Failed to load Pemonlist with status code {}", res->code()); - okTask = std::nullopt; - return *res; - } - - task = web::WebRequest().get(PEMONLIST_URL).map([](web::WebResponse* res2) { - if (res2->ok()) initializePemonlist(res2); - else log::error("Failed to load Pemonlist with status code {}", res2->code()); - - task = std::nullopt; - return *res2; - }); - - okTask = std::nullopt; - return *res; - }); -} - void IntegratedDemonlist::loadPemonlist( EventListener&& listenerRef, EventListener&& okListener, - LoadingCircle* circle, utils::MiniFunction callback + LoadingCircle* circle, std::function const& callback ) { auto&& listener = std::move(listenerRef); listener.bind([callback, circle](web::WebTask::Event* e) { if (auto res = e->getValue()) { - if (res->ok()) { - initializePemonlist(res); - callback(); + if (!res->ok()) { + queueInMainThread([circle, res] { + FLAlertLayer::create(fmt::format("Load Failed ({})", res->code()).c_str(), "Failed to load Pemonlist. Please try again later.", "OK")->show(); + circle->setVisible(false); + }); + return; } - else Loader::get()->queueInMainThread([circle, res] { - FLAlertLayer::create(fmt::format("Load Failed ({})", res->code()).c_str(), "Failed to load Pemonlist. Please try again later.", "OK")->show(); - circle->setVisible(false); - }); + + PEMONLIST_LOADED = true; + PEMONLIST.clear(); + auto str = res->string().value(); + std::string error; + auto json = matjson::parse(str, error).value_or(matjson::Object { { "data", matjson::Array() } }); + if (!error.empty()) log::error("Failed to parse Pemonlist: {}", error); + if (json.is_object() && json.contains("data") && json["data"].is_array()) for (auto const& level : json["data"].as_array()) { + if (!level.contains("level_id") || !level["level_id"].is_number()) continue; + if (!level.contains("name") || !level["name"].is_string()) continue; + if (!level.contains("placement") || !level["placement"].is_number()) continue; + + PEMONLIST.push_back({ + level["level_id"].as_int(), + level["name"].as_string(), + level["placement"].as_int() + }); + } + callback(); } }); isOk(PEMONLIST_UPTIME_URL, std::move(okListener), [&listener, circle](bool ok, int code) { if (ok) listener.setFilter(web::WebRequest().get(PEMONLIST_URL)); - else Loader::get()->queueInMainThread([circle, code] { + else queueInMainThread([circle, code] { FLAlertLayer::create(fmt::format("Load Failed ({})", code).c_str(), "Failed to load Pemonlist. Please try again later.", "OK")->show(); circle->setVisible(false); }); diff --git a/src/IntegratedDemonlist.hpp b/src/IntegratedDemonlist.hpp index 8db4fce..91a3d12 100644 --- a/src/IntegratedDemonlist.hpp +++ b/src/IntegratedDemonlist.hpp @@ -1,8 +1,6 @@ -#include +#pragma once #include -using namespace geode::prelude; - struct IDListDemon { int id; std::string name; @@ -20,14 +18,26 @@ class IntegratedDemonlist { inline static std::vector AREDL = {}; inline static std::vector AREDL_PACKS = {}; inline static std::vector PEMONLIST = {}; - inline static bool TRIED_LOADING = false; + inline static bool AREDL_LOADED = false; + inline static bool PEMONLIST_LOADED = false; - static void initializeAREDL(web::WebResponse*); - static void initializePemonlist(web::WebResponse*); - static void isOk(std::string const&, EventListener&&, MiniFunction callback); - static void loadAREDL(); - static void loadAREDL(EventListener&&, EventListener&&, LoadingCircle*, MiniFunction callback); - static void loadAREDLPacks(EventListener&&, EventListener&&, LoadingCircle*, MiniFunction callback); - static void loadPemonlist(); - static void loadPemonlist(EventListener&&, EventListener&&, LoadingCircle*, MiniFunction callback); + static void isOk(std::string const&, geode::EventListener&&, std::function const&); + static void loadAREDL( + geode::EventListener&&, + geode::EventListener&&, + LoadingCircle*, + std::function const& + ); + static void loadAREDLPacks( + geode::EventListener&&, + geode::EventListener&&, + LoadingCircle*, + std::function const& + ); + static void loadPemonlist( + geode::EventListener&&, + geode::EventListener&&, + LoadingCircle*, + std::function const& + ); }; diff --git a/src/IDListLayer.cpp b/src/classes/IDListLayer.cpp similarity index 95% rename from src/IDListLayer.cpp rename to src/classes/IDListLayer.cpp index 89734fb..4393d34 100644 --- a/src/IDListLayer.cpp +++ b/src/classes/IDListLayer.cpp @@ -1,6 +1,9 @@ #include +#include "../IntegratedDemonlist.hpp" #include "IDListLayer.hpp" +using namespace geode::prelude; + IDListLayer* IDListLayer::create() { auto ret = new IDListLayer(); if (ret->init()) { @@ -104,7 +107,7 @@ bool IDListLayer::init() { m_infoButton->m_title = "All Rated Extreme Demons List"; m_infoButton->m_description = AREDL_INFO; m_fullSearchResults.clear(); - if (!IntegratedDemonlist::AREDL.empty()) page(0); + if (IntegratedDemonlist::AREDL_LOADED) page(0); else IntegratedDemonlist::loadAREDL(std::move(m_aredlListener), std::move(m_aredlOkListener), m_loadingCircle, [this] { page(0); }); }); m_starToggle->setPosition(30.0f, 60.0f); @@ -124,7 +127,7 @@ bool IDListLayer::init() { m_infoButton->m_title = "Pemonlist"; m_infoButton->m_description = PEMONLIST_INFO; m_fullSearchResults.clear(); - if (!IntegratedDemonlist::PEMONLIST.empty()) page(0); + if (IntegratedDemonlist::PEMONLIST_LOADED) page(0); else IntegratedDemonlist::loadPemonlist(std::move(m_pemonlistListener), std::move(m_pemonlistOkListener), m_loadingCircle, [this] { page(0); }); }); m_moonToggle->setPosition(60.0f, 60.0f); @@ -185,10 +188,10 @@ bool IDListLayer::init() { setKeyboardEnabled(true); if (PEMONLIST) { - if (!IntegratedDemonlist::PEMONLIST.empty()) populateList(""); + if (IntegratedDemonlist::PEMONLIST_LOADED) populateList(""); else IntegratedDemonlist::loadPemonlist(std::move(m_pemonlistListener), std::move(m_pemonlistOkListener), m_loadingCircle, [this] { populateList(""); }); } - else if (!IntegratedDemonlist::AREDL.empty()) populateList(""); + else if (IntegratedDemonlist::AREDL_LOADED) populateList(""); else IntegratedDemonlist::loadAREDL(std::move(m_aredlListener), std::move(m_aredlOkListener), m_loadingCircle, [this] { populateList(""); }); return true; @@ -266,10 +269,10 @@ void IDListLayer::populateList(std::string query) { auto searchResults = std::vector(m_fullSearchResults.begin() + m_page * 10, m_fullSearchResults.begin() + std::min((int)m_fullSearchResults.size(), (m_page + 1) * 10)); auto searchObject = GJSearchObject::create(SearchType::MapPackOnClick, string::join(searchResults, ",")); - auto storedLevels = glm->getStoredOnlineLevels(searchObject->getKey()); - if (storedLevels) { - loadLevelsFinished(storedLevels, ""); - setupPageInfo("", ""); + std::string key = searchObject->getKey(); + if (auto storedLevels = glm->getStoredOnlineLevels(key.substr(std::max(0, (int)key.size() - 256)).c_str())) { + loadLevelsFinished(storedLevels, key.c_str()); + setupPageInfo("", key.c_str()); } else glm->getOnlineLevels(searchObject); } @@ -318,7 +321,7 @@ void IDListLayer::search() { m_page = 0; populateList(m_searchBarText); }); - else IntegratedDemonlist::loadAREDL(std::move(m_aredlListener), std::move(m_aredlOkListener),m_loadingCircle, [this] { + else IntegratedDemonlist::loadAREDL(std::move(m_aredlListener), std::move(m_aredlOkListener), m_loadingCircle, [this] { m_page = 0; populateList(m_searchBarText); }); @@ -343,9 +346,8 @@ void IDListLayer::keyDown(enumKeyCodes key) { case CONTROLLER_Right: if (m_rightButton->isVisible()) page(m_page + 1); break; - case KEY_Escape: - case CONTROLLER_B: - keyBackClicked(); + case KEY_Enter: + search(); break; default: CCLayer::keyDown(key); diff --git a/src/IDListLayer.hpp b/src/classes/IDListLayer.hpp similarity index 68% rename from src/IDListLayer.hpp rename to src/classes/IDListLayer.hpp index 9ccc5f1..27ca55c 100644 --- a/src/IDListLayer.hpp +++ b/src/classes/IDListLayer.hpp @@ -1,6 +1,6 @@ -#include "IDPackLayer.hpp" +#include -class IDListLayer : public CCLayer, SetIDPopupDelegate, LevelManagerDelegate { +class IDListLayer : public cocos2d::CCLayer, SetIDPopupDelegate, LevelManagerDelegate { private: inline static bool PEMONLIST = false; inline static const char* AREDL_INFO = @@ -11,26 +11,26 @@ class IDListLayer : public CCLayer, SetIDPopupDelegate, LevelManagerDelegate { "It is managed by camila314, Extatica, IvanCrafter026, Megu, and Voiddle."; public: static IDListLayer* create(); - static CCScene* scene(); + static cocos2d::CCScene* scene(); void search(); void page(int); - void keyDown(enumKeyCodes) override; + void keyDown(cocos2d::enumKeyCodes) override; void keyBackClicked() override; ~IDListLayer() override; protected: - EventListener m_aredlListener; - EventListener m_aredlOkListener; - EventListener m_pemonlistListener; - EventListener m_pemonlistOkListener; + geode::EventListener m_aredlListener; + geode::EventListener m_aredlOkListener; + geode::EventListener m_pemonlistListener; + geode::EventListener m_pemonlistOkListener; GJListLayer* m_list; - CCLabelBMFont* m_listLabel; + cocos2d::CCLabelBMFont* m_listLabel; LoadingCircle* m_loadingCircle; - CCMenu* m_searchBarMenu; - TextInput* m_searchBar; - CCLabelBMFont* m_countLabel; - CCLabelBMFont* m_pageLabel; + cocos2d::CCMenu* m_searchBarMenu; + geode::TextInput* m_searchBar; + cocos2d::CCLabelBMFont* m_countLabel; + cocos2d::CCLabelBMFont* m_pageLabel; InfoAlertButton* m_infoButton; CCMenuItemSpriteExtra* m_backButton; CCMenuItemSpriteExtra* m_leftButton; @@ -50,9 +50,9 @@ class IDListLayer : public CCLayer, SetIDPopupDelegate, LevelManagerDelegate { void addSearchBar(); void showLoading(); void populateList(std::string query); - void loadLevelsFinished(CCArray*, const char*) override; + void loadLevelsFinished(cocos2d::CCArray*, const char*) override; void loadLevelsFailed(const char*) override; - void loadLevelsFinished(CCArray* levels, const char* key, int) override { + void loadLevelsFinished(cocos2d::CCArray* levels, const char* key, int) override { loadLevelsFinished(levels, key); } void loadLevelsFailed(const char* key, int) override { diff --git a/src/IDPackCell.cpp b/src/classes/IDPackCell.cpp similarity index 99% rename from src/IDPackCell.cpp rename to src/classes/IDPackCell.cpp index f597b40..56ac57f 100644 --- a/src/IDPackCell.cpp +++ b/src/classes/IDPackCell.cpp @@ -1,5 +1,7 @@ #include "IDPackCell.hpp" +using namespace geode::prelude; + IDPackCell* IDPackCell::create(IDDemonPack pack) { auto ret = new IDPackCell(); if (ret->init(pack)) { diff --git a/src/IDPackCell.hpp b/src/classes/IDPackCell.hpp similarity index 53% rename from src/IDPackCell.hpp rename to src/classes/IDPackCell.hpp index ded210b..e8b833a 100644 --- a/src/IDPackCell.hpp +++ b/src/classes/IDPackCell.hpp @@ -1,6 +1,6 @@ -#include "IntegratedDemonlist.hpp" +#include "../IntegratedDemonlist.hpp" -class IDPackCell : public CCLayer { +class IDPackCell : public cocos2d::CCLayer { public: static IDPackCell* create(IDDemonPack); protected: diff --git a/src/IDPackLayer.cpp b/src/classes/IDPackLayer.cpp similarity index 99% rename from src/IDPackLayer.cpp rename to src/classes/IDPackLayer.cpp index 4bc04ed..8e53332 100644 --- a/src/IDPackLayer.cpp +++ b/src/classes/IDPackLayer.cpp @@ -1,6 +1,9 @@ #include +#include "IDPackCell.hpp" #include "IDPackLayer.hpp" +using namespace geode::prelude; + IDPackLayer* IDPackLayer::create() { auto ret = new IDPackLayer(); if (ret->init()) { @@ -266,9 +269,8 @@ void IDPackLayer::keyDown(enumKeyCodes key) { case CONTROLLER_Right: if (m_rightButton->isVisible()) page(m_page + 1); break; - case KEY_Escape: - case CONTROLLER_B: - keyBackClicked(); + case KEY_Enter: + search(); break; default: CCLayer::keyDown(key); diff --git a/src/IDPackLayer.hpp b/src/classes/IDPackLayer.hpp similarity index 68% rename from src/IDPackLayer.hpp rename to src/classes/IDPackLayer.hpp index 9a56681..91a0970 100644 --- a/src/IDPackLayer.hpp +++ b/src/classes/IDPackLayer.hpp @@ -1,30 +1,30 @@ -#include "IDPackCell.hpp" +#include "../IntegratedDemonlist.hpp" -class IDPackLayer : public CCLayer, SetIDPopupDelegate { +class IDPackLayer : public cocos2d::CCLayer, SetIDPopupDelegate { private: inline static const char* AREDL_PACK_INFO = "The All Rated Extreme Demons List (AREDL) has packs of extreme demons that are related in some way.\n" "If all levels in a pack are completed, the pack can earn points on aredl.net."; public: static IDPackLayer* create(); - static CCScene* scene(); + static cocos2d::CCScene* scene(); void search(); void page(int); - void keyDown(enumKeyCodes) override; + void keyDown(cocos2d::enumKeyCodes) override; void keyBackClicked() override; ~IDPackLayer() override; protected: - EventListener m_aredlListener; - EventListener m_aredlOkListener; + geode::EventListener m_aredlListener; + geode::EventListener m_aredlOkListener; GJListLayer* m_list; - CCLabelBMFont* m_listLabel; + cocos2d::CCLabelBMFont* m_listLabel; LoadingCircle* m_loadingCircle; - CCMenu* m_searchBarMenu; - TextInput* m_searchBar; - CCLabelBMFont* m_countLabel; - CCLabelBMFont* m_pageLabel; + cocos2d::CCMenu* m_searchBarMenu; + geode::TextInput* m_searchBar; + cocos2d::CCLabelBMFont* m_countLabel; + cocos2d::CCLabelBMFont* m_pageLabel; CCMenuItemSpriteExtra* m_backButton; CCMenuItemSpriteExtra* m_leftButton; CCMenuItemSpriteExtra* m_rightButton; diff --git a/src/hooks/LevelBrowserLayer.cpp b/src/hooks/LevelBrowserLayer.cpp new file mode 100644 index 0000000..a96bf3f --- /dev/null +++ b/src/hooks/LevelBrowserLayer.cpp @@ -0,0 +1,28 @@ +#include "../classes/IDPackLayer.hpp" + +using namespace geode::prelude; + +#include +class $modify(IDLevelBrowserLayer, LevelBrowserLayer) { + bool init(GJSearchObject* object) { + if (!LevelBrowserLayer::init(object)) return false; + + if (object->m_searchType == SearchType::MapPack) { + auto winSize = CCDirector::sharedDirector()->getWinSize(); + auto demonlistButtonSprite = CircleButtonSprite::createWithSprite("ID_demonBtn_001.png"_spr); + demonlistButtonSprite->getTopNode()->setScale(1.0f); + auto demonlistButton = CCMenuItemExt::createSpriteExtra(demonlistButtonSprite, [](auto) { + CCDirector::sharedDirector()->pushScene(CCTransitionFade::create(0.5f, IDPackLayer::scene())); + }); + demonlistButton->setID("demonlist-button"_spr); + auto y = demonlistButtonSprite->getContentHeight() / 2 + 4.0f; + auto menu = CCMenu::create(); + menu->addChild(demonlistButton); + menu->setPosition(winSize.width - y, y); + menu->setID("demonlist-menu"_spr); + addChild(menu, 2); + } + + return true; + } +}; diff --git a/src/hooks/LevelCell.cpp b/src/hooks/LevelCell.cpp new file mode 100644 index 0000000..2f1f25b --- /dev/null +++ b/src/hooks/LevelCell.cpp @@ -0,0 +1,97 @@ +#include "../IntegratedDemonlist.hpp" + +using namespace geode::prelude; + +#define AREDL_LEVEL_URL "https://api.aredl.net/api/aredl/levels/{}" +#define AREDL_LEVEL_2P_URL "https://api.aredl.net/api/aredl/levels/{}?two_player=true" +#define PEMONLIST_LEVEL_URL "https://pemonlist.com/api/level/{}?version=2" + +#include +class $modify(IDLevelCell, LevelCell) { + struct Fields { + EventListener m_soloListener; + EventListener m_dualListener; + }; + + void loadCustomLevelCell() { + LevelCell::loadCustomLevelCell(); + + if (m_level->m_demon.value() <= 0 || !Mod::get()->getSettingValue("enable-rank")) return; + + std::vector positions; + auto platformer = m_level->m_levelLength == 5; + auto& list = platformer ? IntegratedDemonlist::PEMONLIST : IntegratedDemonlist::AREDL; + auto levelID = m_level->m_levelID.value(); + for (auto const& demon : list) { + if (demon.id == levelID) positions.push_back(std::to_string(demon.position)); + } + + if (!positions.empty()) { + addRank(positions, platformer); + return; + } + + auto f = m_fields.self(); + f->m_soloListener.bind([this, f, levelID, platformer](web::WebTask::Event* e) { + if (auto res = e->getValue()) { + if (!res->ok()) return; + auto str = res->string().value(); + std::string error; + auto json = matjson::parse(str, error).value_or(matjson::Object()); + if (!error.empty()) log::error("Failed to parse {} level {}: {}", platformer ? "Pemonlist" : "AREDL", levelID, error); + auto key = platformer ? "placement" : "position"; + if (!json.is_object() || !json.contains(key) || !json[key].is_number()) return; + + auto position1 = json[key].as_int(); + auto& list = platformer ? IntegratedDemonlist::PEMONLIST : IntegratedDemonlist::AREDL; + std::string levelName = m_level->m_levelName; + list.push_back({ levelID, levelName, position1 }); + if (platformer) { + addRank({ std::to_string(position1) }, platformer); + return; + } + + f->m_dualListener.bind([this, levelID, levelName, position1](web::WebTask::Event* e) { + if (auto res = e->getValue()) { + if (!res->ok()) { + addRank({ std::to_string(position1) }, false); + return; + } + + auto str = res->string().value(); + std::string error; + auto json = matjson::parse(str, error).value_or(matjson::Object()); + if (!error.empty()) log::error("Failed to parse AREDL two-player level {}: {}", levelID, error); + if (!json.is_object() || !json.contains("position") || !json["position"].is_number()) return; + + auto position2 = json["position"].as_int(); + IntegratedDemonlist::AREDL.push_back({ levelID, levelName, position2 }); + addRank({ std::to_string(position1), std::to_string(position2) }, false); + } + }); + + f->m_dualListener.setFilter(web::WebRequest().get(fmt::format(AREDL_LEVEL_2P_URL, levelID))); + } + }); + + f->m_soloListener.setFilter(web::WebRequest().get(platformer ? fmt::format(PEMONLIST_LEVEL_URL, levelID) : fmt::format(AREDL_LEVEL_URL, levelID))); + } + + void addRank(std::vector const& positions, bool platformer) { + auto rankTextNode = CCLabelBMFont::create(fmt::format("#{} {}", string::join(positions, "/#"), platformer ? "Pemonlist" : "AREDL").c_str(), "chatFont.fnt"); + auto dailyLevel = m_level->m_dailyID.value() > 0; + rankTextNode->setPosition(346.0f, dailyLevel ? 6.0f : 1.0f); + rankTextNode->setAnchorPoint({ 1.0f, 0.0f }); + rankTextNode->setScale(m_compactView ? 0.45f : 0.6f); + auto isWhite = Mod::get()->getSettingValue("white-rank"); + rankTextNode->setColor(dailyLevel || isWhite ? ccColor3B { 255, 255, 255 } : ccColor3B { 51, 51, 51 }); + rankTextNode->setOpacity(dailyLevel || isWhite ? 200 : 152); + rankTextNode->setID("level-rank-label"_spr); + m_mainLayer->addChild(rankTextNode); + + if (auto levelSizeLabel = m_mainLayer->getChildByID("hiimjustin000.level_size/size-label")) levelSizeLabel->setPosition( + 346.0f - (m_compactView ? rankTextNode->getScaledContentWidth() + 3.0f : 0.0f), + !m_compactView ? 12.0f : 1.0f + ); + } +}; diff --git a/src/hooks/LevelSearchLayer.cpp b/src/hooks/LevelSearchLayer.cpp new file mode 100644 index 0000000..99aceb6 --- /dev/null +++ b/src/hooks/LevelSearchLayer.cpp @@ -0,0 +1,23 @@ +#include "../classes/IDListLayer.hpp" + +using namespace geode::prelude; + +#include +class $modify(IDLevelSearchLayer, LevelSearchLayer) { + bool init(int searchType) { + if (!LevelSearchLayer::init(searchType)) return false; + + auto demonlistButtonSprite = CircleButtonSprite::createWithSprite("ID_demonBtn_001.png"_spr); + demonlistButtonSprite->getTopNode()->setScale(1.0f); + demonlistButtonSprite->setScale(0.8f); + auto demonlistButton = CCMenuItemExt::createSpriteExtra(demonlistButtonSprite, [](auto) { + CCDirector::sharedDirector()->pushScene(CCTransitionFade::create(0.5f, IDListLayer::scene())); + }); + demonlistButton->setID("demonlist-button"_spr); + auto menu = getChildByID("other-filter-menu"); + menu->addChild(demonlistButton); + menu->updateLayout(); + + return true; + } +}; diff --git a/src/main.cpp b/src/main.cpp deleted file mode 100644 index eccf0be..0000000 --- a/src/main.cpp +++ /dev/null @@ -1,115 +0,0 @@ -#include "IDListLayer.hpp" - -#include -class $modify(IDCreatorLayer, CreatorLayer) { - bool init() { - if (!CreatorLayer::init()) return false; - - if (IntegratedDemonlist::TRIED_LOADING) return true; - IntegratedDemonlist::TRIED_LOADING = true; - IntegratedDemonlist::loadAREDL(); - IntegratedDemonlist::loadPemonlist(); - - return true; - } -}; - -#include -class $modify(IDLevelBrowserLayer, LevelBrowserLayer) { - bool init(GJSearchObject* object) { - if (!LevelBrowserLayer::init(object)) return false; - - if (object->m_searchType == SearchType::MapPack) { - auto winSize = CCDirector::sharedDirector()->getWinSize(); - auto demonlistButtonSprite = CircleButtonSprite::createWithSprite("ID_demonBtn_001.png"_spr); - demonlistButtonSprite->getTopNode()->setScale(1.0f); - auto demonlistButton = CCMenuItemExt::createSpriteExtra(demonlistButtonSprite, [](auto) { - CCDirector::sharedDirector()->pushScene(CCTransitionFade::create(0.5f, IDPackLayer::scene())); - }); - demonlistButton->setID("demonlist-button"_spr); - auto y = demonlistButtonSprite->getContentHeight() / 2 + 4.0f; - auto menu = CCMenu::create(); - menu->addChild(demonlistButton); - menu->setPosition(winSize.width - y, y); - menu->setID("demonlist-menu"_spr); - addChild(menu, 2); - } - - return true; - } -}; - -#include -class $modify(IDLevelSearchLayer, LevelSearchLayer) { - bool init(int searchType) { - if (!LevelSearchLayer::init(searchType)) return false; - - auto demonlistButtonSprite = CircleButtonSprite::createWithSprite("ID_demonBtn_001.png"_spr); - demonlistButtonSprite->getTopNode()->setScale(1.0f); - demonlistButtonSprite->setScale(0.8f); - auto demonlistButton = CCMenuItemExt::createSpriteExtra(demonlistButtonSprite, [](auto) { - CCDirector::sharedDirector()->pushScene(CCTransitionFade::create(0.5f, IDListLayer::scene())); - }); - demonlistButton->setID("demonlist-button"_spr); - auto menu = getChildByID("other-filter-menu"); - menu->addChild(demonlistButton); - menu->updateLayout(); - - return true; - } -}; - -// Thanks Cvolton for the code -// https://github.com/Cvolton/betterinfo-geode/blob/v4.0.0/src/hooks/LevelCell.cpp#L113 -#include -class $modify(IDLevelCell, LevelCell) { - void loadCustomLevelCell() { - LevelCell::loadCustomLevelCell(); - - if (m_level->m_demon.value() <= 0 || !Mod::get()->getSettingValue("enable-rank")) return; - - std::vector positions; - auto& list = m_level->m_levelLength == 5 ? IntegratedDemonlist::PEMONLIST : IntegratedDemonlist::AREDL; - for (auto const& demon : list) { - if (demon.id == m_level->m_levelID) positions.push_back(std::to_string(demon.position)); - } - if (positions.empty()) return; - - auto rankTextNode = CCLabelBMFont::create(fmt::format("#{} {}", string::join(positions, "/#"), - m_level->m_levelLength == 5 ? "Pemonlist" : "AREDL").c_str(), "chatFont.fnt"); - rankTextNode->setPosition(346.0f, m_level->m_dailyID.value() > 0 ? 6.0f : 1.0f); - rankTextNode->setAnchorPoint({ 1.0f, 0.0f }); - rankTextNode->setScale(m_compactView ? 0.45f : 0.6f); - if (m_level->m_dailyID.value() > 0 || Mod::get()->getSettingValue("white-rank")) { - rankTextNode->setColor({ 255, 255, 255 }); - rankTextNode->setOpacity(200); - } - else { - rankTextNode->setColor({ 51, 51, 51 }); - rankTextNode->setOpacity(152); - } - rankTextNode->setID("level-rank-label"_spr); - m_mainLayer->addChild(rankTextNode); - } -}; - -#include -class $modify(IDKeyboardDispatcher, CCKeyboardDispatcher) { - bool dispatchKeyboardMSG(enumKeyCodes key, bool down, bool repeat) { - if (key == KEY_Enter && down) { - auto runningScene = CCDirector::sharedDirector()->getRunningScene(); - - if (auto listLayer = static_cast(runningScene->getChildByID("IDListLayer"))) { - listLayer->search(); - return true; - } - - if (auto packLayer = static_cast(runningScene->getChildByID("IDPackLayer"))) { - packLayer->search(); - return true; - } - } - - return CCKeyboardDispatcher::dispatchKeyboardMSG(key, down, repeat); - } -};