From f01d9e3f13b6556d770a59848c68c7aaa1bff8e3 Mon Sep 17 00:00:00 2001 From: FireMario211 <17692105+FireMario211@users.noreply.github.com> Date: Fri, 6 Feb 2026 17:52:32 -0500 Subject: [PATCH 01/32] rewrite (not finished) --- CMakeLists.txt | 38 +- include/RateEvent.hpp | 27 +- include/Types.hpp | 23 + src/MoreLeaderboards/CustomScoreCell.cpp | 436 ------ src/MoreLeaderboards/LevelHelper.cpp | 414 ------ src/MoreLeaderboards/MoreLeaderboards.cpp | 1295 ----------------- src/MoreLeaderboards/MoreLeaderboards.h | 120 -- src/MoreLeaderboards/MoreLeaderboardsCell.cpp | 162 --- src/MoreLeaderboards/MoreLeaderboardsCell.h | 15 - .../MoreLeaderboardsListView.cpp | 75 - .../MoreLeaderboardsListView.h | 14 - src/MoreLeaderboards/Newtab.cpp | 118 -- src/MoreLeaderboards/Newtab.h | 67 - src/MoreLeaderboards/SelectRegion.cpp | 65 - src/MoreLeaderboards/SelectRegion.h | 24 - src/MoreLeaderboards/SelectRegionCell.cpp | 93 -- src/MoreLeaderboards/SelectRegionCell.h | 16 - src/Notifications/DailyChest.cpp | 50 - src/Notifications/DailyChest.h | 11 - src/Notifications/EventsPush.cpp | 916 ------------ src/Notifications/EventsPush.h | 27 - src/OldBorder.cpp | 170 --- src/OldBorder.hpp | 44 - src/ProcessLambdas.cpp | 24 - src/ProcessLambdas.h | 16 - src/Settings/CustomSettings.cpp | 10 - src/Settings/CustomSettings.hpp | 272 ---- src/Settings/TestButton.cpp | 175 --- src/Styles/EventLevelInformation.cpp | 116 -- src/Styles/EventLevelInformation.h | 22 - src/Styles/GauntletStyle.cpp | 1124 -------------- src/Styles/IconLockColors.cpp | 134 -- src/Styles/NewGJCommentList.cpp | 216 --- src/Styles/NewGJCommentList.h | 5 - src/Styles/NewProfilePage.cpp | 344 ----- src/Styles/NewProfilePage.h | 13 - src/Styles/TheVault.cpp | 27 - src/Utils/DemonList.cpp | 205 --- src/Utils/DownloadManager.cpp | 112 -- src/Utils/DownloadManager.h | 30 - src/Utils/FriendsProfilePage.cpp | 216 --- src/Utils/InfoNewLayer.cpp | 39 - src/Utils/InfoNewLayer.h | 9 - src/Utils/ParticleManager.hpp | 56 - src/config.hpp | 6 + src/defs/geode.hpp | 5 + src/defs/misc.hpp | 36 + src/hooks/LevelInfoLayer.cpp | 175 +++ src/hooks/MenuLayer.cpp | 97 ++ .../BadApple.cpp => hooks/SecretLayer2.cpp} | 143 +- src/includes.h | 94 -- src/main.cpp | 933 ++---------- src/modules/QueueHandler.cpp | 10 + src/modules/QueueHandler.hpp | 7 + src/{Utils => modules}/SpotifyHandler.cpp | 64 +- src/modules/events/EventHandler.cpp | 94 ++ src/modules/events/EventHandler.hpp | 119 ++ src/modules/events/EventsPush.cpp | 444 ++++++ src/modules/events/EventsPush.hpp | 58 + src/modules/secrets/BadApple.cpp | 38 + .../secrets/BadApple.hpp} | 5 +- src/{Secret => modules/secrets}/COTE.cpp | 13 +- .../COTE.h => modules/secrets/COTE.hpp} | 7 +- .../secrets}/VideoPlayer.cpp | 0 .../secrets}/VideoPlayer.hpp | 0 src/modules/settings/Credits.cpp | 27 + src/modules/settings/Credits.hpp | 49 + src/modules/settings/Position.cpp | 40 + src/modules/settings/Position.hpp | 90 ++ src/modules/settings/Test.cpp | 157 ++ src/network/AMQClient.cpp | 290 ++++ src/network/AMQClient.hpp | 55 + src/ui/DownloadPopup.cpp | 81 ++ src/ui/DownloadPopup.hpp | 30 + .../credits}/CreditsBetaMenu.cpp | 8 +- .../credits/CreditsBetaMenu.hpp} | 6 +- src/{Settings => ui/credits}/CreditsMenu.cpp | 6 +- .../credits/CreditsMenu.hpp} | 6 +- 78 files changed, 2138 insertions(+), 8440 deletions(-) delete mode 100644 src/MoreLeaderboards/CustomScoreCell.cpp delete mode 100644 src/MoreLeaderboards/LevelHelper.cpp delete mode 100644 src/MoreLeaderboards/MoreLeaderboards.cpp delete mode 100644 src/MoreLeaderboards/MoreLeaderboards.h delete mode 100644 src/MoreLeaderboards/MoreLeaderboardsCell.cpp delete mode 100644 src/MoreLeaderboards/MoreLeaderboardsCell.h delete mode 100644 src/MoreLeaderboards/MoreLeaderboardsListView.cpp delete mode 100644 src/MoreLeaderboards/MoreLeaderboardsListView.h delete mode 100644 src/MoreLeaderboards/Newtab.cpp delete mode 100644 src/MoreLeaderboards/Newtab.h delete mode 100644 src/MoreLeaderboards/SelectRegion.cpp delete mode 100644 src/MoreLeaderboards/SelectRegion.h delete mode 100644 src/MoreLeaderboards/SelectRegionCell.cpp delete mode 100644 src/MoreLeaderboards/SelectRegionCell.h delete mode 100644 src/Notifications/DailyChest.cpp delete mode 100644 src/Notifications/DailyChest.h delete mode 100644 src/Notifications/EventsPush.cpp delete mode 100644 src/Notifications/EventsPush.h delete mode 100644 src/OldBorder.cpp delete mode 100644 src/OldBorder.hpp delete mode 100644 src/ProcessLambdas.cpp delete mode 100644 src/ProcessLambdas.h delete mode 100644 src/Settings/CustomSettings.cpp delete mode 100644 src/Settings/CustomSettings.hpp delete mode 100644 src/Settings/TestButton.cpp delete mode 100644 src/Styles/EventLevelInformation.cpp delete mode 100644 src/Styles/EventLevelInformation.h delete mode 100644 src/Styles/GauntletStyle.cpp delete mode 100644 src/Styles/IconLockColors.cpp delete mode 100644 src/Styles/NewGJCommentList.cpp delete mode 100644 src/Styles/NewGJCommentList.h delete mode 100644 src/Styles/NewProfilePage.cpp delete mode 100644 src/Styles/NewProfilePage.h delete mode 100644 src/Styles/TheVault.cpp delete mode 100644 src/Utils/DemonList.cpp delete mode 100644 src/Utils/DownloadManager.cpp delete mode 100644 src/Utils/DownloadManager.h delete mode 100644 src/Utils/FriendsProfilePage.cpp delete mode 100644 src/Utils/InfoNewLayer.cpp delete mode 100644 src/Utils/InfoNewLayer.h delete mode 100644 src/Utils/ParticleManager.hpp create mode 100644 src/config.hpp create mode 100644 src/defs/geode.hpp create mode 100644 src/defs/misc.hpp create mode 100644 src/hooks/LevelInfoLayer.cpp create mode 100644 src/hooks/MenuLayer.cpp rename src/{Secret/BadApple.cpp => hooks/SecretLayer2.cpp} (55%) delete mode 100644 src/includes.h create mode 100644 src/modules/QueueHandler.cpp create mode 100644 src/modules/QueueHandler.hpp rename src/{Utils => modules}/SpotifyHandler.cpp (87%) create mode 100644 src/modules/events/EventHandler.cpp create mode 100644 src/modules/events/EventHandler.hpp create mode 100644 src/modules/events/EventsPush.cpp create mode 100644 src/modules/events/EventsPush.hpp create mode 100644 src/modules/secrets/BadApple.cpp rename src/{Secret/BadApple.h => modules/secrets/BadApple.hpp} (65%) rename src/{Secret => modules/secrets}/COTE.cpp (79%) rename src/{Secret/COTE.h => modules/secrets/COTE.hpp} (64%) rename src/{Secret => modules/secrets}/VideoPlayer.cpp (100%) rename src/{Secret => modules/secrets}/VideoPlayer.hpp (100%) create mode 100644 src/modules/settings/Credits.cpp create mode 100644 src/modules/settings/Credits.hpp create mode 100644 src/modules/settings/Position.cpp create mode 100644 src/modules/settings/Position.hpp create mode 100644 src/modules/settings/Test.cpp create mode 100644 src/network/AMQClient.cpp create mode 100644 src/network/AMQClient.hpp create mode 100644 src/ui/DownloadPopup.cpp create mode 100644 src/ui/DownloadPopup.hpp rename src/{Settings => ui/credits}/CreditsBetaMenu.cpp (96%) rename src/{Settings/CreditsBetaMenu.h => ui/credits/CreditsBetaMenu.hpp} (74%) rename src/{Settings => ui/credits}/CreditsMenu.cpp (96%) rename src/{Settings/CreditsMenu.h => ui/credits/CreditsMenu.hpp} (67%) diff --git a/CMakeLists.txt b/CMakeLists.txt index aecf372..5e61ed3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,39 +14,10 @@ endif() set(CMAKE_CXX_VISIBILITY_PRESET hidden) include(cmake/CPM.cmake) -if(NOT DEFINED GEODE_TARGET_PLATFORM) - set(GEODE_TARGET_PLATFORM "Win64") - set(GEODE_GD_VERSION 2.2074) - set(GEODE_COMP_GD_VERSION 22074) -endif() - -if (${GEODE_TARGET_PLATFORM} STREQUAL "Win64") - set(GEODE_COMPILER "clang-cl") - set(GEODE_CCACHE_VARIANT "sccache") - #set(GEODE_TRIPLE "x86_64-unknown-windows-msvc") - if(WIN32) # force it! - set(CMAKE_SYSTEM_PROCESSOR x86_64) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m64") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m64") - endif() -endif() - set(PROJECT_NAME GDUtils) project(${PROJECT_NAME} VERSION 1.0.0) -# Use GLOB_RECURSE instead of GLOB -# to recursively add all source files -# under src/ -file(GLOB SOURCES - src/*.cpp - src/MoreLeaderboards/*.cpp - src/Notifications/*.cpp - src/Secret/*.cpp - src/Settings/*.cpp - src/Utils/*.cpp - src/Styles/*.cpp - include/*.cpp -) +file(GLOB_RECURSE SOURCES CONFIGURE_DEPENDS src/*.cpp) # Set up the mod binary add_library(${PROJECT_NAME} SHARED ${SOURCES}) @@ -66,13 +37,14 @@ set(BUILD_SHARED_LIBS OFF) #CPMAddPackage("gh:libuv/libuv#c1a9f01") #CPMAddPackage("gh:CopernicaMarketingSoftware/AMQP-CPP#ca49382") - +CPMAddPackage("gh:camila314/uibuilder#main") # i love it target_include_directories(${PROJECT_NAME} PRIVATE libs/filesystem libs/pl_mpeg/ libs/glew/ ${rabbitmq-c_SOURCE_DIR}/include + src/ #${amqpcpp_SOURCE_DIR}/src #${amqpcpp_SOURCE_DIR}/include #${libuv_SOURCE_DIR}/include @@ -87,10 +59,10 @@ endif() add_subdirectory($ENV{GEODE_SDK} ${CMAKE_CURRENT_BINARY_DIR}/geode) if (APPLE AND NOT (${CMAKE_SYSTEM_NAME} STREQUAL "iOS" OR IOS)) - target_link_libraries(${PROJECT_NAME} rabbitmq::rabbitmq-static "-framework OpenGL") + target_link_libraries(${PROJECT_NAME} UIBuilder rabbitmq::rabbitmq-static "-framework OpenGL") #target_link_libraries(${PROJECT_NAME} uv_a amqpcpp "-framework OpenGL") else() - target_link_libraries(${PROJECT_NAME} rabbitmq::rabbitmq-static) + target_link_libraries(${PROJECT_NAME} UIBuilder rabbitmq::rabbitmq-static) #target_link_libraries(${PROJECT_NAME} uv_a amqpcpp) endif() if (CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux" AND WIN32) diff --git a/include/RateEvent.hpp b/include/RateEvent.hpp index e314872..4acfb9b 100644 --- a/include/RateEvent.hpp +++ b/include/RateEvent.hpp @@ -4,19 +4,12 @@ #include "Types.hpp" namespace GDUtils::Events { - class RateEvent : public geode::Event { - protected: - EventData m_eventData; + class RateEvent : public geode::SimpleEvent { public: - RateEvent(EventData data) : m_eventData(data) {}; - - EventType getEventType() const { return m_eventData.type; }; - std::string getLevelName() const { return m_eventData.level_name; }; - int getLevelID() const { return m_eventData.level_id; }; + using SimpleEvent::SimpleEvent; static void emit(EventData data) { - RateEvent(data).post(); + RateEvent().send(data); }; - EventData getData() const { return m_eventData; }; static EventType intToType(int type) { switch (type) { default: @@ -62,17 +55,7 @@ namespace GDUtils::Events { } } }; - - class OnRate : public geode::Event { - protected: - EventData m_eventData; - public: - OnRate(EventData data) : m_eventData(data) {}; - - std::string getTitle() const { return m_eventData.title; }; - EventType getEventType() const { return m_eventData.type; }; - std::string getLevelName() const { return m_eventData.level_name; }; - int getLevelID() const { return m_eventData.level_id; }; - EventData getData() const { return m_eventData; }; + struct OnRate final : geode::GlobalEvent { + using GlobalEvent::GlobalEvent; }; } diff --git a/include/Types.hpp b/include/Types.hpp index f810ae9..3f686a8 100644 --- a/include/Types.hpp +++ b/include/Types.hpp @@ -28,6 +28,10 @@ struct EventData { int level_id = -1; std::string levels_list; int maxToCompleteList = 0; + std::string getTitle() const { return title; }; + EventType getEventType() const { return type; }; + std::string getLevelName() const { return level_name; }; + int getLevelID() const { return level_id; }; }; template<> @@ -110,4 +114,23 @@ struct matjson::Serialize { } return geode::Ok(data); } + static matjson::Value toJson(EventData const& value) { + auto val = Value::object(); + val.set("demon", value.demon); + val.set("starsum", value.starsum); + val.set("starsum", value.starsum); + val.set("stars", value.stars); + val.set("rate", value.rate); + val.set("type", (int)value.type); + val.set("title", value.title); + val.set("level_name", value.level_name); + val.set("level_creator", value.level_creator); + val.set("coins", value.coins); + val.set("verified_coins", value.verified_coins); + val.set("platformer", value.platformer); + val.set("level_id", value.level_id); + val.set("levels_list", value.levels_list); + val.set("maxToCompleteList", value.maxToCompleteList); + return val; + } }; diff --git a/src/MoreLeaderboards/CustomScoreCell.cpp b/src/MoreLeaderboards/CustomScoreCell.cpp deleted file mode 100644 index 267f06d..0000000 --- a/src/MoreLeaderboards/CustomScoreCell.cpp +++ /dev/null @@ -1,436 +0,0 @@ -#include -#include -#include "MoreLeaderboards.h" -#include "SelectRegion.h" -#include "LevelHelper.cpp" - -#include -using namespace geode::prelude; - - -class $modify(CustomScoreCell, GJScoreCell) { - void loadFromScore(GJUserScore* score) { - GJScoreCell::loadFromScore(score); - - CCLayer* layer = as(this->getChildren()->objectAtIndex(1)); - if (layer) { - // Search for flag - std::string data_flag = MoreLeaderboards::data_response_moreLB; - - if (data_flag == "") return; - - auto position = layer->getPosition(); - - RowLayout* layout = RowLayout::create(); - layout->setGap(3); - layout->setAxisAlignment(AxisAlignment::Start); - layout->setCrossAxisOverflow(true); - - CCMenu* menu = CCMenu::create(); - menu->setContentSize(CCSize(283, 21.580f)); - menu->setPosition(206, 42); - - // Move child to menu with layout - auto starsLabel = as(layer->getChildByIDRecursive("stars-label")); - starsLabel->setLayoutOptions( - AxisLayoutOptions::create()->setAutoScale(true)->setScaleLimits(0, 0.5F)->setScalePriority(1) - ); - auto starsIcon = as(layer->getChildByIDRecursive("stars-icon")); - starsIcon->setLayoutOptions( - AxisLayoutOptions::create()->setAutoScale(true)->setScaleLimits(0, 0.5F)->setScalePriority(1) - ); - auto moonsLabel = as(layer->getChildByIDRecursive("moons-label")); - moonsLabel->setLayoutOptions( - AxisLayoutOptions::create()->setAutoScale(true)->setScaleLimits(0, 0.5F)->setScalePriority(1) - ); - auto moonsIcon = as(layer->getChildByIDRecursive("moons-icon")); - moonsIcon->setLayoutOptions( - AxisLayoutOptions::create()->setAutoScale(true)->setScaleLimits(0, 0.5F)->setScalePriority(1) - ); - - // Username control - std::string playerName = as(as(layer->getChildByIDRecursive("player-name"))->getChildren()->objectAtIndex(0))->getString(); - CCMenu* menu_playername = CCMenu::create(); - menu_playername->setContentSize(as(layer->getChildByIDRecursive("player-name"))->getContentSize()); - menu_playername->setLayout( - RowLayout::create()->setGap(3)->setAxisAlignment(AxisAlignment::Start) - ); - auto playerNameBM = CCLabelBMFont::create(playerName.c_str(), "goldFont.fnt"); - auto playerNameBtn = CCMenuItemSpriteExtra::create( - playerNameBM, - this, - (SEL_MenuHandler)&CustomScoreCell::onViewProfile - ); - playerNameBtn->setLayoutOptions( - AxisLayoutOptions::create()->setAutoScale(true)->setScaleLimits(0.5F, 0.8F)->setScalePriority(1) - ); - - std::vector dataString = MoreLeaderboards::getWords(data_flag, "#"); - std::vector users = MoreLeaderboards::getWords(dataString[0], "|"); - - std::string count_diff = "0"; - - while (users.size() > 0) { - std::string user = users[0]; - - std::vector data_info = MoreLeaderboards::getWords(user, ":"); - while (data_info.size() > 0) { - std::string id = data_info[0]; - std::string info = data_info[1]; - - if (id == "1") { - if (info != playerName) break; - } - if (id == "9989") { - count_diff = info; - } - if (id == "9990") { - if (info == "XX") break; - auto flagSpr = CCSprite::createWithSpriteFrameName((""_spr+info+".png").c_str()); - flagSpr->setPosition({ 20, 15 }); - flagSpr->setScaleY(20 / flagSpr->getContentHeight() * 0.50); - flagSpr->setScaleX(std::min(1.0f, 30 / flagSpr->getContentWidth())); - flagSpr->setLayoutOptions( - AxisLayoutOptions::create()->setAutoScale(true)->setScalePriority(1) - ); - menu_playername->addChild(flagSpr); - } - - data_info.erase(data_info.begin()); - data_info.erase(data_info.begin()); - } - - users.erase(users.begin()); - }; - - menu_playername->addChild(playerNameBtn); - menu_playername->updateLayout(); - - OPT(layer->getChildByID("stars-label"))->removeFromParentAndCleanup(true); - OPT(layer->getChildByID("stars-icon"))->removeFromParentAndCleanup(true); - OPT(layer->getChildByID("moons-label"))->removeFromParentAndCleanup(true); - OPT(layer->getChildByID("moons-icon"))->removeFromParentAndCleanup(true); - OPT(layer->getChildByID("main-menu"))->removeFromParentAndCleanup(true); - - menu->addChild(menu_playername); - menu->addChild(starsLabel); - menu->addChild(starsIcon); - menu->addChild(moonsLabel); - menu->addChild(moonsIcon); - - menu->setLayout(layout); - menu->updateLayout(); - - // get main layer - CCLayer* mainLayer = nullptr; - CCObject* obj; - CCARRAY_FOREACH(this->getChildren(), obj) { - CCLayer* search = typeinfo_cast(obj); - if (search != nullptr && search->getChildrenCount() > 2) { - mainLayer = search; - break; - } - } - auto statsMenu = mainLayer->getChildByIDRecursive("stats-menu"); - - // BetterProgression part - Loader* loader = Loader::get(); - if (!loader->isModLoaded("itzkiba.better_progression") && MoreLeaderboards::g_tab == StatsListType::BetterProgression) { - int currentEXP = LevelHelper::calculateTotalEXP(score); - int currentLevel = LevelHelper::getLevelFromEXP(currentEXP); - - auto badgeSpr = SpriteHelper::createFullSprite(currentLevel); - badgeSpr->setPosition({75, 17.5}); - badgeSpr->setScale(0.62); - - mainLayer->addChild(badgeSpr); - - statsMenu->setScale(0.83); - statsMenu->setPositionX(statsMenu->getPositionX() + 13.f); - } - - // Classic & Platformer Demons part - auto classic_sprite = CCSprite::createWithSpriteFrameName("star_small01_001.png"); - classic_sprite->setPositionX(30); - classic_sprite->setScale(1.2f); - - auto platformer_sprite = CCSprite::createWithSpriteFrameName("moon_small01_001.png"); - platformer_sprite->setPositionX(30); - platformer_sprite->setScale(1.2f); - - auto pointercrate = CCSprite::createWithSpriteFrameName("diffIcon_06_btn_001.png"); - auto pointercrate_text = CCLabelBMFont::create("PC", "goldFont.fnt"); - pointercrate_text->setPosition(28, 0); - pointercrate_text->setScale(0.5f); - pointercrate->addChild(pointercrate_text); - - auto easydemon_sprite_classic = CCSprite::createWithSpriteFrameName("diffIcon_07_btn_001.png"); - easydemon_sprite_classic->addChild(classic_sprite); - auto mediumdemon_sprite_classic = CCSprite::createWithSpriteFrameName("diffIcon_08_btn_001.png"); - mediumdemon_sprite_classic->addChild(classic_sprite); - auto harddemon_sprite_classic = CCSprite::createWithSpriteFrameName("diffIcon_06_btn_001.png"); - harddemon_sprite_classic->addChild(classic_sprite); - auto insane_sprite_classic = CCSprite::createWithSpriteFrameName("diffIcon_09_btn_001.png"); - insane_sprite_classic->addChild(classic_sprite); - auto extreme_sprite_classic = CCSprite::createWithSpriteFrameName("diffIcon_10_btn_001.png"); - extreme_sprite_classic->addChild(classic_sprite); - - auto easydemon_sprite_platformer = CCSprite::createWithSpriteFrameName("diffIcon_07_btn_001.png"); - easydemon_sprite_platformer->addChild(platformer_sprite); - auto mediumdemon_sprite_platformer = CCSprite::createWithSpriteFrameName("diffIcon_08_btn_001.png"); - mediumdemon_sprite_platformer->addChild(platformer_sprite); - auto harddemon_sprite_platformer = CCSprite::createWithSpriteFrameName("diffIcon_06_btn_001.png"); - harddemon_sprite_platformer->addChild(platformer_sprite); - auto insane_sprite_platformer = CCSprite::createWithSpriteFrameName("diffIcon_09_btn_001.png"); - insane_sprite_platformer->addChild(platformer_sprite); - auto extreme_sprite_platformer = CCSprite::createWithSpriteFrameName("diffIcon_10_btn_001.png"); - extreme_sprite_platformer->addChild(platformer_sprite); - - // Change Z orders - statsMenu->getChildByIDRecursive("diamonds-label")->setZOrder(0); - statsMenu->getChildByIDRecursive("diamonds-icon")->setZOrder(1); - statsMenu->getChildByIDRecursive("coins-label")->setZOrder(2); - statsMenu->getChildByIDRecursive("coins-icon")->setZOrder(3); - statsMenu->getChildByIDRecursive("user-coins-label")->setZOrder(4); - statsMenu->getChildByIDRecursive("user-coins-icon")->setZOrder(5); - statsMenu->getChildByIDRecursive("demons-label")->setZOrder(6); - statsMenu->getChildByIDRecursive("demons-icon")->setZOrder(7); - auto cp_label = statsMenu->getChildByIDRecursive("creator-points-label"); - if (cp_label) cp_label->setZOrder(8); - auto cp_icon = statsMenu->getChildByIDRecursive("creator-points-icon"); - if (cp_icon) cp_icon->setZOrder(9); - - auto demon_icon = mainLayer->getChildByIDRecursive("demons-icon"); - auto demon_icon_pos = demon_icon->getPosition(); - auto demon_icon_scale = demon_icon->getScale(); - auto demon_icon_z = demon_icon->getZOrder(); - auto demon_icon_tag = demon_icon->getTag(); - auto demon_icon_layout = demon_icon->getLayoutOptions(); - auto demon_label = mainLayer->getChildByIDRecursive("demons-label"); - auto demon_label_pos = demon_label->getPosition(); - auto demon_label_scale = demon_label->getScale(); - auto demon_label_z = demon_label->getZOrder(); - auto demon_label_tag = demon_label->getTag(); - auto demon_label_layout = demon_label->getLayoutOptions(); - - switch (MoreLeaderboards::g_tab) { - case StatsListType::platformerDemonsEasy: - demon_icon->removeFromParent(); - demon_icon = easydemon_sprite_platformer; - demon_icon->setPosition(demon_icon_pos); - demon_icon->setScale(demon_icon_scale); - demon_icon->setLayoutOptions(demon_icon_layout); - demon_icon->setTag(demon_icon_tag); - demon_icon->setZOrder(demon_icon_z); - statsMenu->addChild(easydemon_sprite_platformer); - - demon_label->removeFromParent(); - demon_label = CCLabelBMFont::create(count_diff.c_str(), "bigFont.fnt"); - demon_label->setPosition(demon_label_pos); - demon_label->setScale(demon_label_scale); - demon_label->setZOrder(demon_label_z); - demon_label->setTag(demon_label_tag); - demon_label->setLayoutOptions(demon_label_layout); - statsMenu->addChild(demon_label); - break; - case StatsListType::platformerDemonsMedium: - demon_icon->removeFromParent(); - demon_icon = mediumdemon_sprite_platformer; - demon_icon->setPosition(demon_icon_pos); - demon_icon->setScale(demon_icon_scale); - demon_icon->setLayoutOptions(demon_icon_layout); - demon_icon->setTag(demon_icon_tag); - demon_icon->setZOrder(demon_icon_z); - statsMenu->addChild(mediumdemon_sprite_platformer); - - demon_label->removeFromParent(); - demon_label = CCLabelBMFont::create(count_diff.c_str(), "bigFont.fnt"); - demon_label->setPosition(demon_label_pos); - demon_label->setScale(demon_label_scale); - demon_label->setZOrder(demon_label_z); - demon_label->setTag(demon_label_tag); - demon_label->setLayoutOptions(demon_label_layout); - statsMenu->addChild(demon_label); - break; - case StatsListType::platformerDemonsHard: - demon_icon->removeFromParent(); - demon_icon = harddemon_sprite_platformer; - demon_icon->setPosition(demon_icon_pos); - demon_icon->setScale(demon_icon_scale); - demon_icon->setLayoutOptions(demon_icon_layout); - demon_icon->setTag(demon_icon_tag); - demon_icon->setZOrder(demon_icon_z); - statsMenu->addChild(harddemon_sprite_platformer); - - demon_label->removeFromParent(); - demon_label = CCLabelBMFont::create(count_diff.c_str(), "bigFont.fnt"); - demon_label->setPosition(demon_label_pos); - demon_label->setScale(demon_label_scale); - demon_label->setZOrder(demon_label_z); - demon_label->setTag(demon_label_tag); - demon_label->setLayoutOptions(demon_label_layout); - statsMenu->addChild(demon_label); - break; - case StatsListType::platformerDemonsInsane: - demon_icon->removeFromParent(); - demon_icon = insane_sprite_platformer; - demon_icon->setPosition(demon_icon_pos); - demon_icon->setScale(demon_icon_scale); - demon_icon->setLayoutOptions(demon_icon_layout); - demon_icon->setTag(demon_icon_tag); - demon_icon->setZOrder(demon_icon_z); - statsMenu->addChild(insane_sprite_platformer); - - demon_label->removeFromParent(); - demon_label = CCLabelBMFont::create(count_diff.c_str(), "bigFont.fnt"); - demon_label->setPosition(demon_label_pos); - demon_label->setScale(demon_label_scale); - demon_label->setZOrder(demon_label_z); - demon_label->setTag(demon_label_tag); - demon_label->setLayoutOptions(demon_label_layout); - statsMenu->addChild(demon_label); - break; - case StatsListType::platformerDemonsExtreme: - demon_icon->removeFromParent(); - demon_icon = extreme_sprite_platformer; - demon_icon->setPosition(demon_icon_pos); - demon_icon->setScale(demon_icon_scale); - demon_icon->setLayoutOptions(demon_icon_layout); - demon_icon->setTag(demon_icon_tag); - demon_icon->setZOrder(demon_icon_z); - statsMenu->addChild(extreme_sprite_platformer); - - demon_label->removeFromParent(); - demon_label = CCLabelBMFont::create(count_diff.c_str(), "bigFont.fnt"); - demon_label->setPosition(demon_label_pos); - demon_label->setScale(demon_label_scale); - demon_label->setZOrder(demon_label_z); - demon_label->setTag(demon_label_tag); - demon_label->setLayoutOptions(demon_label_layout); - statsMenu->addChild(demon_label); - break; - case StatsListType::classicDemonsEasy: - demon_icon->removeFromParent(); - demon_icon = easydemon_sprite_classic; - demon_icon->setPosition(demon_icon_pos); - demon_icon->setScale(demon_icon_scale); - demon_icon->setLayoutOptions(demon_icon_layout); - demon_icon->setTag(demon_icon_tag); - demon_icon->setZOrder(demon_icon_z); - statsMenu->addChild(easydemon_sprite_classic); - - demon_label->removeFromParent(); - demon_label = CCLabelBMFont::create(count_diff.c_str(), "bigFont.fnt"); - demon_label->setPosition(demon_label_pos); - demon_label->setScale(demon_label_scale); - demon_label->setZOrder(demon_label_z); - demon_label->setTag(demon_label_tag); - demon_label->setLayoutOptions(demon_label_layout); - statsMenu->addChild(demon_label); - break; - case StatsListType::classicDemonsMedium: - demon_icon->removeFromParent(); - demon_icon = mediumdemon_sprite_classic; - demon_icon->setPosition(demon_icon_pos); - demon_icon->setScale(demon_icon_scale); - demon_icon->setLayoutOptions(demon_icon_layout); - demon_icon->setTag(demon_icon_tag); - demon_icon->setZOrder(demon_icon_z); - statsMenu->addChild(mediumdemon_sprite_classic); - - demon_label->removeFromParent(); - demon_label = CCLabelBMFont::create(count_diff.c_str(), "bigFont.fnt"); - demon_label->setPosition(demon_label_pos); - demon_label->setScale(demon_label_scale); - demon_label->setZOrder(demon_label_z); - demon_label->setTag(demon_label_tag); - demon_label->setLayoutOptions(demon_label_layout); - statsMenu->addChild(demon_label); - break; - case StatsListType::classicDemonsHard: - demon_icon->removeFromParent(); - demon_icon = harddemon_sprite_classic; - demon_icon->setPosition(demon_icon_pos); - demon_icon->setScale(demon_icon_scale); - demon_icon->setLayoutOptions(demon_icon_layout); - demon_icon->setTag(demon_icon_tag); - demon_icon->setZOrder(demon_icon_z); - statsMenu->addChild(harddemon_sprite_classic); - - demon_label->removeFromParent(); - demon_label = CCLabelBMFont::create(count_diff.c_str(), "bigFont.fnt"); - demon_label->setPosition(demon_label_pos); - demon_label->setScale(demon_label_scale); - demon_label->setZOrder(demon_label_z); - demon_label->setTag(demon_label_tag); - demon_label->setLayoutOptions(demon_label_layout); - statsMenu->addChild(demon_label); - break; - case StatsListType::classicDemonsInsane: - demon_icon->removeFromParent(); - demon_icon = insane_sprite_classic; - demon_icon->setPosition(demon_icon_pos); - demon_icon->setScale(demon_icon_scale); - demon_icon->setLayoutOptions(demon_icon_layout); - demon_icon->setTag(demon_icon_tag); - demon_icon->setZOrder(demon_icon_z); - statsMenu->addChild(insane_sprite_classic); - - demon_label->removeFromParent(); - demon_label = CCLabelBMFont::create(count_diff.c_str(), "bigFont.fnt"); - demon_label->setPosition(demon_label_pos); - demon_label->setScale(demon_label_scale); - demon_label->setZOrder(demon_label_z); - demon_label->setTag(demon_label_tag); - demon_label->setLayoutOptions(demon_label_layout); - statsMenu->addChild(demon_label); - break; - case StatsListType::classicDemonsExtreme: - demon_icon->removeFromParent(); - demon_icon = extreme_sprite_classic; - demon_icon->setPosition(demon_icon_pos); - demon_icon->setScale(demon_icon_scale); - demon_icon->setLayoutOptions(demon_icon_layout); - demon_icon->setTag(demon_icon_tag); - demon_icon->setZOrder(demon_icon_z); - statsMenu->addChild(extreme_sprite_classic); - - demon_label->removeFromParent(); - demon_label = CCLabelBMFont::create(count_diff.c_str(), "bigFont.fnt"); - demon_label->setPosition(demon_label_pos); - demon_label->setScale(demon_label_scale); - demon_label->setZOrder(demon_label_z); - demon_label->setTag(demon_label_tag); - demon_label->setLayoutOptions(demon_label_layout); - statsMenu->addChild(demon_label); - break; - case StatsListType::Pointercrate: - demon_icon->removeFromParent(); - demon_icon = pointercrate; - demon_icon->setPosition(demon_icon_pos); - demon_icon->setScale(demon_icon_scale); - demon_icon->setLayoutOptions(demon_icon_layout); - demon_icon->setTag(demon_icon_tag); - demon_icon->setZOrder(demon_icon_z); - statsMenu->addChild(pointercrate); - - demon_label->removeFromParent(); - demon_label = CCLabelBMFont::create(count_diff.c_str(), "bigFont.fnt"); - demon_label->setPosition(demon_label_pos); - demon_label->setScale(demon_label_scale); - demon_label->setZOrder(demon_label_z); - demon_label->setTag(demon_label_tag); - demon_label->setLayoutOptions(demon_label_layout); - statsMenu->addChild(demon_label); - break; - default: - break; - } - - statsMenu->updateLayout(); - - layer->addChild(menu); - layer->updateLayout(); - } - } -}; diff --git a/src/MoreLeaderboards/LevelHelper.cpp b/src/MoreLeaderboards/LevelHelper.cpp deleted file mode 100644 index 28f7454..0000000 --- a/src/MoreLeaderboards/LevelHelper.cpp +++ /dev/null @@ -1,414 +0,0 @@ -#include "../includes.h" - -#define STAR_EXP 5 -#define MOON_EXP 5 -#define DIAMOND_EXP 2 -#define GOLD_COIN_EXP 100 -#define USER_COIN_EXP 20 -#define DEMON_EXP 75 -#define CP_EXP 5000 - -class LevelHelper { - public: - inline static int calculateTotalEXP(GJUserScore* score) { - int totalEXP = 0; - totalEXP += (STAR_EXP * score->m_stars); - totalEXP += (MOON_EXP * score->m_moons); - totalEXP += (DIAMOND_EXP * score->m_diamonds); - totalEXP += (GOLD_COIN_EXP * score->m_secretCoins); - totalEXP += (USER_COIN_EXP * score->m_userCoins); - totalEXP += (DEMON_EXP * score->m_demons); - totalEXP += (CP_EXP * score->m_creatorPoints); - - return totalEXP; - } - - inline static int getEXPRequiredForLevel(int level) { - return (50 * (level * level) + (50 * level)); - } - - inline static int getLevelFromEXP(int exp) { - return (sqrt(2*exp + 25) - 5) / 10.0; - } -}; - -class SpriteHelper { - public: - inline static CCSprite* createFullSprite(int currentLevel) { - auto badgeSprite = SpriteHelper::getBadgeFromLevel(currentLevel); - badgeSprite->setID("itzkiba.better_progression/tier-badge"); - badgeSprite->setZOrder(10); - - auto badgeLabel = CCLabelBMFont::create(std::to_string(currentLevel).c_str(), "bigFont.fnt"); - badgeLabel->setZOrder(11); - badgeLabel->setScale((currentLevel >= 100 ? 0.3 : 0.4)); - badgeLabel->setPosition({badgeSprite->getContentSize().width / 2, badgeSprite->getContentSize().height / 2 + 1.f}); - badgeSprite->addChild(badgeLabel); - - auto badgeShadow = CCSprite::createWithSpriteFrameName("itzkiba.better_progression/shadow.png"); - badgeShadow->setZOrder(-5); - badgeShadow->setScale(0.9); - badgeShadow->setPosition({badgeSprite->getContentSize().width / 2, badgeSprite->getContentSize().height / 2}); - badgeShadow->setOpacity(100); - badgeSprite->addChild(badgeShadow); - - auto badgeGlow = SpriteHelper::getGlowFromLevel(currentLevel); - if (badgeGlow != nullptr && currentLevel <= 350) { - badgeGlow->setZOrder(-3); - badgeGlow->setPosition({badgeSprite->getContentSize().width / 2, badgeSprite->getContentSize().height / 2}); - badgeGlow->setScaleX(0.975); - badgeGlow->setScaleY(0.95); - badgeSprite->addChild(badgeGlow); - - ccBlendFunc blending = {GL_ONE, GL_ONE}; - badgeGlow->setBlendFunc(blending); - - if (currentLevel >= 200 && currentLevel <= 224) - badgeGlow->setColor({ 255, 156, 110 }); - if (currentLevel >= 225 && currentLevel <= 249) - badgeGlow->setColor({ 90, 255, 148 }); - if (currentLevel >= 250 && currentLevel <= 274) - badgeGlow->setColor({ 65, 239, 255 }); - if (currentLevel >= 275 && currentLevel <= 299) - badgeGlow->setColor({ 245, 108, 255 }); - - auto fadein = CCEaseSineInOut::create(CCFadeTo::create(0.9f, 50)); - auto fadeout = CCEaseSineInOut::create(CCFadeTo::create(0.9f, 255)); - auto sequence = CCSequence::create(fadein, fadeout, nullptr); - auto repeatSequence = CCRepeatForever::create(sequence); - - badgeGlow->runAction(repeatSequence); - } - - return badgeSprite; - } - - inline static CCSprite* getBadgeFromLevel(int level) { - if (level >= 0 && level <= 4) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/tier0_0.png"); - } - if (level >= 5 && level <= 9) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/tier0_1.png"); - } - if (level >= 10 && level <= 14) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/tier0_2.png"); - } - if (level >= 15 && level <= 19) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/tier0_3.png"); - } - if (level >= 20 && level <= 24) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/tier0_4.png"); - } - - if (level >= 25 && level <= 29) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/tier1_0.png"); - } - if (level >= 30 && level <= 34) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/tier1_1.png"); - } - if (level >= 35 && level <= 39) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/tier1_2.png"); - } - if (level >= 40 && level <= 44) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/tier1_3.png"); - } - if (level >= 45 && level <= 49) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/tier1_4.png"); - } - - if (level >= 50 && level <= 54) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/tier2_0.png"); - } - if (level >= 55 && level <= 59) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/tier2_1.png"); - } - if (level >= 60 && level <= 64) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/tier2_2.png"); - } - if (level >= 65 && level <= 69) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/tier2_3.png"); - } - if (level >= 70 && level <= 74) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/tier2_4.png"); - } - - if (level >= 75 && level <= 79) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/tier3_0.png"); - } - if (level >= 80 && level <= 84) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/tier3_1.png"); - } - if (level >= 85 && level <= 89) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/tier3_2.png"); - } - if (level >= 90 && level <= 94) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/tier3_3.png"); - } - if (level >= 95 && level <= 99) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/tier3_4.png"); - } - - if (level >= 100 && level <= 104) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/tier4_0.png"); - } - if (level >= 105 && level <= 109) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/tier4_1.png"); - } - if (level >= 110 && level <= 114) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/tier4_2.png"); - } - if (level >= 115 && level <= 119) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/tier4_3.png"); - } - if (level >= 120 && level <= 124) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/tier4_4.png"); - } - - if (level >= 125 && level <= 129) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/tier5_0.png"); - } - if (level >= 130 && level <= 134) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/tier5_1.png"); - } - if (level >= 135 && level <= 139) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/tier5_2.png"); - } - if (level >= 140 && level <= 144) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/tier5_3.png"); - } - if (level >= 145 && level <= 149) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/tier5_4.png"); - } - - if (level >= 150 && level <= 154) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/tier6_0.png"); - } - if (level >= 155 && level <= 159) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/tier6_1.png"); - } - if (level >= 160 && level <= 164) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/tier6_2.png"); - } - if (level >= 165 && level <= 169) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/tier6_3.png"); - } - if (level >= 170 && level <= 174) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/tier6_4.png"); - } - - if (level >= 175 && level <= 179) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/tier7_0.png"); - } - if (level >= 180 && level <= 184) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/tier7_1.png"); - } - if (level >= 185 && level <= 189) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/tier7_2.png"); - } - if (level >= 190 && level <= 194) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/tier7_3.png"); - } - if (level >= 195 && level <= 199) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/tier7_4.png"); - } - - if (level >= 200 && level <= 204) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/tier8_0.png"); - } - if (level >= 205 && level <= 209) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/tier8_1.png"); - } - if (level >= 210 && level <= 214) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/tier8_2.png"); - } - if (level >= 215 && level <= 219) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/tier8_3.png"); - } - if (level >= 220 && level <= 224) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/tier8_4.png"); - } - - if (level >= 225 && level <= 229) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/tier9_0.png"); - } - if (level >= 230 && level <= 234) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/tier9_1.png"); - } - if (level >= 235 && level <= 239) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/tier9_2.png"); - } - if (level >= 240 && level <= 244) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/tier9_3.png"); - } - if (level >= 245 && level <= 249) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/tier9_4.png"); - } - - if (level >= 250 && level <= 254) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/tier10_0.png"); - } - if (level >= 255 && level <= 259) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/tier10_1.png"); - } - if (level >= 260 && level <= 264) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/tier10_2.png"); - } - if (level >= 265 && level <= 269) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/tier10_3.png"); - } - if (level >= 270 && level <= 274) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/tier10_4.png"); - } - - if (level >= 275 && level <= 279) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/tier11_0.png"); - } - if (level >= 280 && level <= 284) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/tier11_1.png"); - } - if (level >= 285 && level <= 289) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/tier11_2.png"); - } - if (level >= 290 && level <= 294) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/tier11_3.png"); - } - if (level >= 295 && level <= 299) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/tier11_4.png"); - } - if (level >= 300 && level <= 349) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/tier12.png"); - } - // TEMP - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/tier0_0.png"); - } - - inline static CCSprite* getGlowFromLevel(int level) { - if (level >= 200 && level <= 204) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/glow_0.png"); - } - if (level >= 205 && level <= 209) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/glow_1.png"); - } - if (level >= 210 && level <= 214) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/glow_2.png"); - } - if (level >= 215 && level <= 219) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/glow_3.png"); - } - if (level >= 220 && level <= 224) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/glow_4.png"); - } - - if (level >= 225 && level <= 229) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/glow_0.png"); - } - if (level >= 230 && level <= 234) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/glow_1.png"); - } - if (level >= 235 && level <= 239) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/glow_2.png"); - } - if (level >= 240 && level <= 244) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/glow_3.png"); - } - if (level >= 245 && level <= 249) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/glow_4.png"); - } - - if (level >= 250 && level <= 254) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/glow_0.png"); - } - if (level >= 255 && level <= 259) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/glow_1.png"); - } - if (level >= 260 && level <= 264) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/glow_2.png"); - } - if (level >= 265 && level <= 269) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/glow_3.png"); - } - if (level >= 270 && level <= 274) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/glow_4.png"); - } - - if (level >= 275 && level <= 279) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/glow_0.png"); - } - if (level >= 280 && level <= 284) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/glow_1.png"); - } - if (level >= 285 && level <= 289) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/glow_2.png"); - } - if (level >= 290 && level <= 294) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/glow_3.png"); - } - if (level >= 295 && level <= 299) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/glow_4.png"); - } - if (level >= 300 && level <= 349) { - return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/glow_4.png"); - } - - return nullptr; - } - - inline static CCSprite* getMostEXPIcon(GJUserScore* score) { - int starEXP = score->m_stars * STAR_EXP; - int moonEXP = score->m_moons * MOON_EXP; - int diamondEXP = score->m_diamonds * DIAMOND_EXP; - int secretCoinEXP = score->m_secretCoins * GOLD_COIN_EXP; - int userCoinEXP = score->m_userCoins * USER_COIN_EXP; - int demonEXP = score->m_demons * DEMON_EXP; - int cpEXP = score->m_creatorPoints * CP_EXP; - - if (std::max({starEXP, moonEXP, diamondEXP, secretCoinEXP, userCoinEXP, demonEXP, cpEXP}) == starEXP) - return CCSprite::createWithSpriteFrameName("GJ_starsIcon_001.png"); - if (std::max({starEXP, moonEXP, diamondEXP, secretCoinEXP, userCoinEXP, demonEXP, cpEXP}) == moonEXP) - return CCSprite::createWithSpriteFrameName("GJ_moonsIcon_001.png"); - if (std::max({starEXP, moonEXP, diamondEXP, secretCoinEXP, userCoinEXP, demonEXP, cpEXP}) == diamondEXP) - return CCSprite::createWithSpriteFrameName("GJ_diamondsIcon_001.png"); - if (std::max({starEXP, moonEXP, diamondEXP, secretCoinEXP, userCoinEXP, demonEXP, cpEXP}) == secretCoinEXP) - return CCSprite::createWithSpriteFrameName("GJ_coinsIcon_001.png"); - if (std::max({starEXP, moonEXP, diamondEXP, secretCoinEXP, userCoinEXP, demonEXP, cpEXP}) == userCoinEXP) - return CCSprite::createWithSpriteFrameName("GJ_coinsIcon2_001.png"); - if (std::max({starEXP, moonEXP, diamondEXP, secretCoinEXP, userCoinEXP, demonEXP, cpEXP}) == demonEXP) - return CCSprite::createWithSpriteFrameName("GJ_demonIcon_001.png"); - if (std::max({starEXP, moonEXP, diamondEXP, secretCoinEXP, userCoinEXP, demonEXP, cpEXP}) == cpEXP) - return CCSprite::createWithSpriteFrameName("GJ_hammerIcon_001.png"); - return nullptr; - } - - inline static ccColor3B getColorFromLevel(int currentLevel) { - ccColor3B barColor = {0, 0, 0}; - if (currentLevel >= 0 && currentLevel <= 24) - barColor = { 156, 156, 156 }; - if (currentLevel >= 25 && currentLevel <= 49) - barColor = { 228, 228, 228 }; - if (currentLevel >= 50 && currentLevel <= 74) - barColor = { 135, 255, 145 }; - if (currentLevel >= 75 && currentLevel <= 99) - barColor = { 83, 166, 255 }; - if (currentLevel >= 100 && currentLevel <= 124) - barColor = { 234, 117, 255 }; - if (currentLevel >= 125 && currentLevel <= 149) - barColor = { 255, 117, 177 }; - if (currentLevel >= 150 && currentLevel <= 174) - barColor = { 255, 156, 117 }; - if (currentLevel >= 175 && currentLevel <= 199) - barColor = { 255, 238, 106 }; - if (currentLevel >= 200 && currentLevel <= 224) - barColor = { 255, 0, 89 }; - if (currentLevel >= 225 && currentLevel <= 249) - barColor = { 145, 255, 0 }; - if (currentLevel >= 250 && currentLevel <= 274) - barColor = { 0, 255, 255 }; - if (currentLevel >= 275) - barColor = { 247, 0, 255 }; - if (currentLevel >= 300) { - barColor = {255, 150, 150}; - } - - return barColor; - } -}; diff --git a/src/MoreLeaderboards/MoreLeaderboards.cpp b/src/MoreLeaderboards/MoreLeaderboards.cpp deleted file mode 100644 index f34759a..0000000 --- a/src/MoreLeaderboards/MoreLeaderboards.cpp +++ /dev/null @@ -1,1295 +0,0 @@ -#include "MoreLeaderboards.h" -#include "Newtab.h" -#include -#include -#include - -StatsListType MoreLeaderboards::g_tab = StatsListType::Stars; -bool MoreLeaderboards::modFilter = false; -int MoreLeaderboards::modFilterType = 0; -std::string MoreLeaderboards::username = ""; -int MoreLeaderboards::scroll_int = 0; -static int page = 0; -static int start_count = 0; -static int end_count = 0; -static int total_count = 0; -std::string MoreLeaderboards::data_response_moreLB = ""; - -EventListener m_listener; - -std::vector MoreLeaderboards::getWords(std::string s, std::string delim) { - std::vector res; - std::string token = ""; - for (int i = 0; i < s.size(); i++) { - bool flag = true; - for (int j = 0; j < delim.size(); j++) { - if (s[i + j] != delim[j]) flag = false; - } - if (flag) { - if (token.size() > 0) { - res.push_back(token); - token = ""; - i += delim.size() - 1; - } - } else { - token += s[i]; - } - } - res.push_back(token); - return res; -} - - -class SearchUserLBLayer : public Popup { - protected: - bool setup(MoreLeaderboards* layer) { - m_layer = layer; - input_username->setMaxCharCount(20); - input_username->setPlaceholder(""); - input_username->setPositionY(10); - - m_buttonMenu->addChildAtPosition(input_username, Anchor::Center); - auto winSize = cocos2d::CCDirector::sharedDirector()->getWinSize(); - auto validate_spr = ButtonSprite::create("Search", 60, true, "bigFont.fnt", "GJ_button_01.png", 30, .5F); - auto validate_btn = CCMenuItemSpriteExtra::create( - validate_spr, - this, - menu_selector(SearchUserLBLayer::onValidate) - ); - m_buttonMenu->addChildAtPosition(validate_btn, Anchor::Center, {0, -35}); - - if (MoreLeaderboards::username != "") { - input_username->setString(MoreLeaderboards::username); - - auto delete_spr = CCSprite::createWithSpriteFrameName("GJ_deleteBtn_001.png"); - delete_spr->setScale(.45f); - auto delete_btn = CCMenuItemSpriteExtra::create( - delete_spr, - this, - menu_selector(SearchUserLBLayer::onRemoveUsername) - ); - m_buttonMenu->addChildAtPosition(delete_btn, Anchor::Right, {-17, 0}); - } - return true; - } - void onValidate(cocos2d::CCObject*); - void onRemoveUsername(cocos2d::CCObject*); - public: - MoreLeaderboards* m_layer; - TextInput* input_username = TextInput::create(200.0F, "Username", "bigFont.fnt"); - static SearchUserLBLayer* create(MoreLeaderboards* layer) { - auto ret = new SearchUserLBLayer(); - if (ret->initAnchored(260.f, 60.f, layer)) { - ret->autorelease(); - return ret; - } - delete ret; - return nullptr; - } -}; - -void SearchUserLBLayer::onValidate(CCObject* pSender) { - if (this->m_layer->loading) return; - - MoreLeaderboards::username = input_username->getString(); - - this->m_layer->changeTabPage(); - - this->m_layer->onTab(nullptr); - - Popup::onClose(pSender); -} - -void SearchUserLBLayer::onRemoveUsername(CCObject* pSender) { - MoreLeaderboards::username = ""; - MoreLeaderboards::scroll_int = 0; - input_username->setString(""); - - this->m_layer->changeTabPage(); - this->m_layer->onTab(nullptr); - - Popup::onClose(pSender); -} - -CCDictionary* MoreLeaderboards::responseToDict(const std::string& response){ - auto dict = CCDictionary::create(); - std::stringstream responseStream(response); - std::string currentKey; - std::string keyID; - std::string playerID; - std::string username; - std::string accountID; - - GameLevelManager* glm = GameLevelManager::sharedState(); - unsigned int i = 0; - while(getline(responseStream, currentKey, ':')){ - if(i % 2 == 0) { - keyID = currentKey; - } else { - if (keyID == "2") { // Player ID - playerID = currentKey; - } else if (keyID == "16") { - accountID = currentKey; - } else if (keyID == "1") { - username = currentKey; - } - dict->setObject(CCString::create(currentKey.c_str()),keyID); - } - i++; - } - if (playerID.length() > 0 && username.length() > 0 && accountID.length() > 0) { - glm->storeUserName(numFromString(playerID).unwrapOrDefault(), numFromString(accountID).unwrapOrDefault(), username); - } - return dict; -} - -void MoreLeaderboards::onMoreLeaderboards(CCObject* pSender) { - auto scene = CCScene::create(); - auto layer = MoreLeaderboards::create("more"); - scene->addChild(layer); - auto transitionFade = CCTransitionFade::create(0.5, scene); - CCDirector::sharedDirector()->pushScene(transitionFade); -} - -void MoreLeaderboards::onModsList(CCObject* pSender) { - auto scene = CCScene::create(); - auto layer = MoreLeaderboards::create("mods"); - scene->addChild(layer); - auto transitionFade = CCTransitionFade::create(0.5, scene); - CCDirector::sharedDirector()->pushScene(transitionFade); -} - -void MoreLeaderboards::onRegion(CCObject* pSender) { - SelectRegion::scene([this](std::string id) { - country_id = id; - - if (loading) return; - - loading = true; - - loading_circle = LoadingCircle::create(); - loading_circle->setZOrder(25); - loading_circle->setParentLayer(this); - loading_circle->show(); - - if (displayedData) { - displayedData->release(); - displayedData = cocos2d::CCArray::create(); - displayedData->retain(); - } - - page = 0; - - if (loading) { - startLoadingMore(); - loadPageMore(); - } - - resetInfos(); - }); - /*geode::createQuickPopup( - "Coming soon!", - R"text( -Filter by country leaderboards is coming soon on GDUtils! -Stay tuned for updates via our Discord server on the page of the mod in the Geode Index! - )text", - "OK", "Mod page", - [](auto, bool btn2) { - if (btn2) { - openIndexPopup(Mod::get()); - } - } - );*/ -} - -void MoreLeaderboards::onInfo(CCObject* pSender) { - FLAlertLayer::create( - nullptr, - "More Leaderboards", - R"text( -More Leaderboards is a feature that allows you to view more leaderboards than the ones in the game. -You can view leaderboards for Diamonds, User Coins, Demons, Moons, and Creators Points of people that are registered on Updated Leaderboards project. - -Thanks to ChiefWoof & the Helper team for providing the data for this feature! - )text", - "OK", - nullptr, - 400.0f - )->show(); -} - -void MoreLeaderboards::onLBInfo(CCObject* pSender) { - geode::createQuickPopup( - "Submit your user stats", - R"text( -You can submit your user stats to the Updated Leaderboards team by joining their Discord server and following the instructions right there. - )text", - "OK", "Discord Server", - [](auto, bool btn2) { - if (btn2) { - web::openLinkInBrowser("https://discord.gg/mZnDm886sR"); - } - } - ); -} - -MoreLeaderboards* MoreLeaderboards::create(std::string type) { - auto pRet = new MoreLeaderboards(); - SelectRegionCell::country_id = "NAN"; - if (pRet && pRet->init(type)) { - pRet->autorelease(); - return pRet; - } - CC_SAFE_DELETE(pRet); - return nullptr; -} - -bool MoreLeaderboards::init(std::string type) { - auto director = cocos2d::CCDirector::sharedDirector(); - auto size = director->getWinSize(); - - displayedData = cocos2d::CCArray::create(); - displayedData->retain(); - - auto background = CCSprite::create("GJ_gradientBG.png"); - auto backgroundSize = background->getContentSize(); - - background->setScaleX(size.width / backgroundSize.width); - background->setScaleY(size.height / backgroundSize.height); - background->setAnchorPoint({0, 0}); - background->setColor({0, 101, 253}); - background->setZOrder(-1); - this->addChild(background); - - // Corners - CCSprite* corner_left = CCSprite::createWithSpriteFrameName("GJ_sideArt_001.png"); - CCSprite* corner_right = CCSprite::createWithSpriteFrameName("GJ_sideArt_001.png"); - corner_right->setRotation(-90); - corner_left->setPosition({35, 35}); - corner_right->setPosition({size.width - 35, 35}); - this->addChild(corner_left); - this->addChild(corner_right); - - CCSprite* button = cocos2d::CCSprite::createWithSpriteFrameName("GJ_arrow_01_001.png"); - auto backButton = CCMenuItemSpriteExtra::create(button, this, menu_selector(MoreLeaderboards::backButton)); - CCMenu* menu = cocos2d::CCMenu::create(); - menu->addChild(backButton); - menu->setPosition({ 25, size.height - 25 }); - this->addChild(menu); - - if (type == "more") { - // Change region - auto menu_region = CCMenu::create(); - - auto regionSpr = CCSprite::create("earth_btn.png"_spr);; - regionSpr->setScale(.8f); - auto regionBtn = CCMenuItemSpriteExtra::create( - regionSpr, - this, - menu_selector(MoreLeaderboards::onRegion) - ); - regionBtn->setPosition(239, -52); - menu_region->addChild(regionBtn); - - auto infoSpr = cocos2d::CCSprite::createWithSpriteFrameName("GJ_infoIcon_001.png"); - auto infoBtn = CCMenuItemSpriteExtra::create( - infoSpr, - this, - menu_selector(MoreLeaderboards::onInfo) - ); - infoBtn->setPosition(-239, -120); - menu_region->addChild(infoBtn); - - auto lbSpr = cocos2d::CCSprite::createWithSpriteFrameName("GJ_levelLeaderboardBtn_001.png"); - auto lbBtn = CCMenuItemSpriteExtra::create( - lbSpr, - this, - menu_selector(MoreLeaderboards::onLBInfo) - ); - lbBtn->setPosition(-239, -57); - menu_region->addChild(lbBtn); - - this->addChild(menu_region); - - tab_page = 0; - - // tabs gradient - #ifndef GEODE_IS_IOS - m_tabsGradientNode = CCClippingNode::create(); - #else - m_tabsGradientNode = CCNode::create(); - #endif - m_tabsGradientNode->setContentSize(this->getContentSize()); - m_tabsGradientNode->setAnchorPoint({0.5f, 0.5f}); - m_tabsGradientNode->ignoreAnchorPointForPosition(true); - m_tabsGradientNode->setZOrder(0); - m_tabsGradientNode->setScale(0.8f); - #ifndef GEODE_IS_IOS - m_tabsGradientNode->setInverted(false); - m_tabsGradientNode->setAlphaThreshold(0.7f); - #endif - - m_tabsGradientSprite = CCSprite::create("tab-gradient.png"_spr); - m_tabsGradientNode->addChild(m_tabsGradientSprite); - - m_tabsGradientStencil = CCSprite::create("tab-gradient-mask.png"_spr); - m_tabsGradientStencil->setAnchorPoint({0.f, 0.f}); - m_tabsGradientStencil->setColor({172, 255, 67}); - m_tabsGradientStencil->setZOrder(1); - m_tabsGradientStencil->setScale(0.8f); - - #ifndef GEODE_IS_IOS - m_tabsGradientNode->setStencil(m_tabsGradientStencil); - #endif - - this->addChild(m_tabsGradientNode); - this->addChild(m_tabsGradientStencil); - - changeTabPage(); - - // select first tab - this->onTab(nullptr); - } else if (type == "mods") { - loading_circle = LoadingCircle::create(); - loading_circle->setZOrder(25); - loading_circle->setParentLayer(this); - loading_circle->show(); - - startLoadingMods(); - loadPageMods(); - } - setTouchEnabled(true); - setKeypadEnabled(true); - return true; -} - -void MoreLeaderboards::backButton(cocos2d::CCObject*) { - MoreLeaderboards::data_response_moreLB = ""; - MoreLeaderboards::g_tab = StatsListType::Stars; - MoreLeaderboards::username = ""; - MoreLeaderboards::scroll_int = 0; - MoreLeaderboards::modFilter = false; - MoreLeaderboards::modFilterType = 0; - cocos2d::CCDirector::sharedDirector()->popSceneWithTransition(0.5F, cocos2d::PopTransition::kPopTransitionFade); -}; - -void MoreLeaderboards::keyBackClicked() { - backButton(CCNode::create()); -} - -void MoreLeaderboards::fadeLoadingCircle() { - if (loading_circle == nullptr) return; - loading_circle->fadeAndRemove(); -}; - -void MoreLeaderboards::handle_request_mods(std::string const& data) { - if(!displayedData) { displayedData = CCArray::create(); displayedData->retain(); }; - if (data != "-1") { - displayedData = CCArray::create(); - - std::vector mods = getWords(data, "|"); - - while (mods.size() > 0) { - std::string mod = mods[0]; - CCDictionary* modDict = CCDictionary::create(); - modDict->setObject(cocos2d::CCString::create(mod), "modstring"); - displayedData->addObject(modDict); - mods.erase(mods.begin()); - }; - } - loadPageMods(); -} - -void MoreLeaderboards::startLoadingMods() { - const std::function then = [this](std::string const& data) { - handle_request_mods(data); - fadeLoadingCircle(); - }; - const std::function expect = [this](std::string const& error) { - fadeLoadingCircle(); - }; - - geode::utils::web::WebRequest request = web::WebRequest(); - m_listener.bind([expect = std::move(expect), then = std::move(then)](web::WebTask::Event* e) { - if (web::WebResponse* res = e->getValue()) { - if (res->ok()) { - then(res->string().unwrapOrDefault()); - } else { - expect("An error occured while sending a request on our server. Please try again later."); - } - } - }); - m_listener.setFilter(request.get("https://clarifygdps.com/gdutils/modslist.php")); -}; - -void MoreLeaderboards::loadPageMods() { - if(listLayer != nullptr) listLayer->removeFromParentAndCleanup(true); - - auto winSize = CCDirector::sharedDirector()->getWinSize(); - - leaderboardView = MoreLeaderboardsListView::create(displayedData, 356.f, 220.f); - listLayer = GJListLayer::create(leaderboardView, "GD Moderators", {191, 114, 62, 255}, 356.f, 220.f, 0); - listLayer->setPosition(winSize / 2 - listLayer->getScaledContentSize() / 2 - CCPoint(0,5)); - - addChild(listLayer); -} - -void MoreLeaderboards::startLoadingMore() { - geode::utils::web::WebRequest request = web::WebRequest(); - - this->retain(); - - const std::function expect = [this](std::string const& error) { - loading = false; - auto scene = CCDirector::sharedDirector()->getRunningScene(); - auto layer = scene->getChildren()->objectAtIndex(0); - if (layer == nullptr) return this->release(); - if (misc::getNodeName(layer) != "MoreLeaderboards") return this->release(); - geode::createQuickPopup( - "Error", - "An error occured while sending a request on our server. Please try again later.", - "OK", nullptr, - [this](auto, bool btn2) { - if (!btn2) { - MoreLeaderboards::g_tab = StatsListType::Stars; - keyBackClicked(); - } - } - ); - fadeLoadingCircle(); - loading = false; - this->release(); - }; - - const std::function then = [this](std::string const& data) { - if (data != "-1") { - data_region = data; - SelectRegion::displayedData = MoreLeaderboards::getWords(data, "|"); - } - - std::string type = ""; - geode::utils::web::WebRequest request = web::WebRequest(); - - switch (MoreLeaderboards::g_tab) { - case StatsListType::Diamonds: - type = "diamonds"; - break; - case StatsListType::UserCoins: - type = "ucoins"; - break; - case StatsListType::Demons: - type = "demons"; - break; - case StatsListType::Moons: - type = "moons"; - break; - case StatsListType::Creators: - type = "cp"; - break; - case StatsListType::Stars: - type = "stars"; - break; - case StatsListType::classicDemonsEasy: - type = "classicDemonsEasy"; - break; - case StatsListType::classicDemonsMedium: - type = "classicDemonsMedium"; - break; - case StatsListType::classicDemonsHard: - type = "classicDemonsHard"; - break; - case StatsListType::classicDemonsInsane: - type = "classicDemonsInsane"; - break; - case StatsListType::classicDemonsExtreme: - type = "classicDemonsExtreme"; - break; - case StatsListType::platformerDemonsEasy: - type = "platformerDemonsEasy"; - break; - case StatsListType::platformerDemonsMedium: - type = "platformerDemonsMedium"; - break; - case StatsListType::platformerDemonsHard: - type = "platformerDemonsHard"; - break; - case StatsListType::platformerDemonsInsane: - type = "platformerDemonsInsane"; - break; - case StatsListType::platformerDemonsExtreme: - type = "platformerDemonsExtreme"; - break; - case StatsListType::BetterProgression: - type = "betterProgression"; - break; - case StatsListType::Pointercrate: - type = "pointercratePoints"; - break; - } - - const std::function expect = [this](std::string const& error) { - loading = false; - - auto scene = CCDirector::sharedDirector()->getRunningScene(); - auto layer = scene->getChildren()->objectAtIndex(0); - if (layer == nullptr) return this->release(); - if (misc::getNodeName(layer) != "MoreLeaderboards") return this->release(); - geode::createQuickPopup( - "Error", - "An error occured while sending a request on our server. Please try again later.", - "OK", nullptr, - [this](auto, bool btn2) { - if (!btn2) { - MoreLeaderboards::g_tab = StatsListType::Stars; - keyBackClicked(); - } - } - ); - fadeLoadingCircle(); - loading = false; - this->release(); - }; - - const std::function then = [this](std::string const& data) { - loading = false; - - auto scene = CCDirector::sharedDirector()->getRunningScene(); - auto layer = scene->getChildren()->objectAtIndex(0); - if (layer == nullptr) return this->release(); - if (misc::getNodeName(layer) != "MoreLeaderboards") return this->release(); // prevent le crash, even though the layer shouldve already been destructed - if (data == "-1" || data.length() < 2) { - fadeLoadingCircle(); - geode::createQuickPopup( - "Error", - "An error occured while sending a request on our server. Please try again later.", - "OK", nullptr, - [this](auto, bool btn2) { - if (!btn2) { - MoreLeaderboards::g_tab = StatsListType::Stars; - keyBackClicked(); - } - } - ); - this->release(); - return; - } - if (data == "-2" || data.length() < 2) { - fadeLoadingCircle(); - FLAlertLayer::create(nullptr, - "Error", - "User not found : " + MoreLeaderboards::username, - "OK", - nullptr, - 200.0F - )->show(); - - MoreLeaderboards::scroll_int = 0; - this->release(); - - loadTabPageButtons(); - return; - } - - handle_request_more(data); - fadeLoadingCircle(); - loading = false; - - this->release(); - }; - - m_listener.bind([expect = std::move(expect), then = std::move(then)](web::WebTask::Event* e) { - if (web::WebResponse* response = e->getValue()) { - if (response->ok()) { - then(response->string().unwrap()); - } else { - expect("An error occurred while sending a request on our server. Please try again later."); - } - } - }); - - m_listener.setFilter( - request.param("type", type) - .param("page", page) - .param("country", country_id) - .param("username", username) - .param("mod", (modFilter ? "1" : "0")) - .param("modFilter", modFilterType) - .param("version", "1.0") - .get("https://clarifygdps.com/gdutils/moreleaderboards.php") - ); - }; - - if (data_region == "") { - m_listener.bind([expect = std::move(expect), then = std::move(then)] (web::WebTask::Event* e) { - if (web::WebResponse* res = e->getValue()) { - if (res->ok()) { - then(res->string().unwrap()); - } else { - expect("An error occured while sending a request on our server. Please try again later."); - } - } - }); - - m_listener.setFilter(request.get("https://clarifygdps.com/gdutils/moreleaderboards_region.php")); - } else { - then(data_region); - } -}; - -void MoreLeaderboards::handle_request_more(std::string const& data) { - if(!displayedData) { displayedData = CCArray::create(); displayedData->retain(); }; - - if (data != "-1") { - data_response_moreLB = data; - - displayedData = CCArray::create(); - std::vector dataString = getWords(data, "#"); - std::vector users = getWords(dataString[0], "|"); - std::vector data_page = getWords(dataString[1], "|"); - - while (users.size() > 0) { - std::string user = users[0]; - - auto score = GJUserScore::create( - MoreLeaderboards::responseToDict(user) - ); - displayedData->addObject(score); - users.erase(users.begin()); - }; - - int id = 0; - while (data_page.size() > 0) { - int page_test = numFromString(data_page[0]).unwrapOrDefault(); - - if (id == 0) { - start_count = page_test; - } else if (id == 1) { - end_count = page_test; - } else if (id == 2) { - total_count = page_test; - } else if (id == 3) { - page = page_test; - } else if (id == 4) { - if (page_test != 0) { - MoreLeaderboards::scroll_int = page_test; - } - } - - id++; - data_page.erase(data_page.begin()); - } - } - - loadPageMore(); - loadPageStats(); -} - -void MoreLeaderboards::loadPageMore() { - if(listLayer != nullptr) listLayer->removeFromParentAndCleanup(true); - - auto winSize = CCDirector::sharedDirector()->getWinSize(); - leaderboardViewScore = CustomListView::create(displayedData, nullptr, 220.f, 356.f, 0, BoomListType::Score, 0.0F); - listLayer = GJListLayer::create(leaderboardViewScore, nullptr, {191, 114, 62, 255}, 356.f, 220.f, 0); - listLayer->setZOrder(31); - listLayer->setPosition(winSize / 2 - listLayer->getScaledContentSize() / 2 - CCPoint(0,5)); - - addChild(listLayer); - - if (MoreLeaderboards::scroll_int != 0) { - listLayer->m_listView->m_tableView->scrollLayer((float)-INT_MAX); - listLayer->m_listView->m_tableView->scrollLayer(MoreLeaderboards::scroll_int); - } -} - -void MoreLeaderboards::onSearch(CCObject*) { - SearchUserLBLayer::create(this)->show(); -} - -void MoreLeaderboards::loadPageStats() { - auto scene = CCDirector::sharedDirector()->getRunningScene(); - auto winSize = CCDirector::sharedDirector()->getWinSize(); - - if (page_label != nullptr) page_label->removeFromParentAndCleanup(true); - if (this->getChildByIDRecursive("menu_label") != nullptr) this->getChildByIDRecursive("menu_label")->removeFromParentAndCleanup(true); - if (this->getChildByIDRecursive("menu_page") != nullptr) this->getChildByIDRecursive("menu_page")->removeFromParentAndCleanup(true); - - CCMenu* menu_label = CCMenu::create(); - menu_label->setID("menu_label"); - menu_label->setLayout( - RowLayout::create() - ->setAxisAlignment(AxisAlignment::Center) - ->setGap(10.f) - ); - menu_label->setPosition({ winSize.width / 2, 12.f }); - menu_label->setContentSize({ 300.f, 7.f }); - - std::string fmt = fmt::format("Top {} - {} of {}", start_count, end_count, total_count); - page_label = CCLabelBMFont::create(fmt.c_str(), "goldFont.fnt"); - page_label->setZOrder(40); - page_label->setLayoutOptions( - AxisLayoutOptions::create() - ->setAutoScale(true) - ->setScaleLimits(0, 0.7F) - ->setScalePriority(1) - ); - menu_label->addChild(page_label); - - trophy = CCSprite::createWithSpriteFrameName("rankIcon_top10_001.png"); - trophy->setScale(.3f); - trophy->setPosition(CCPoint { -5.f, 0.f } - page_label->getScaledContentSize().width); - menu_label->addChild(trophy); - - addChild(menu_label); - menu_label->updateLayout(); - - CCMenu* menu = CCMenu::create(); - menu->setID("menu_page"); - - if (page_left != nullptr) page_left->removeFromParentAndCleanup(true); - if (page_right != nullptr) page_right->removeFromParentAndCleanup(true); - - if (page > 0) { - page_left = CCMenuItemSpriteExtra::create( - CCSprite::createWithSpriteFrameName("GJ_arrow_02_001.png"), - this, - menu_selector(MoreLeaderboards::onPageLeft) - ); - page_left->setPosition(-220, 0); - menu->addChild(page_left); - } - - if (end_count < total_count) { - auto sprite = CCSprite::createWithSpriteFrameName("GJ_arrow_02_001.png"); - sprite->setFlipX(true); - page_right = CCMenuItemSpriteExtra::create( - sprite, - this, - menu_selector(MoreLeaderboards::onPageRight) - ); - page_right->setPosition(220, 0); - menu->addChild(page_right); - } - - addChild(menu); - - loadTabPageButtons(); -} - -void MoreLeaderboards::loadTabPageButtons() { - if (tab_page > 0) { - auto tab_page_left_sprite = CCSprite::createWithSpriteFrameName("GJ_arrow_03_001.png"); - tab_page_left_sprite->setScale(0.6f); - tab_page_left = CCMenuItemSpriteExtra::create( - tab_page_left_sprite, - this, - menu_selector(MoreLeaderboards::onTabPageLeft) - ); - tab_page_left->setPosition(-184, 138); - m_menu->addChild(tab_page_left); - } - - if (tab_page < 2) { - auto tab_page_right_sprite = CCSprite::createWithSpriteFrameName("GJ_arrow_03_001.png"); - tab_page_right_sprite->setScale(0.6f); - tab_page_right_sprite->setFlipX(true); - tab_page_right = CCMenuItemSpriteExtra::create( - tab_page_right_sprite, - this, - menu_selector(MoreLeaderboards::onTabPageRight) - ); - tab_page_right->setPosition(184, 138); - m_menu->addChild(tab_page_right); - } - - auto searchSpr = CCSprite::createWithSpriteFrameName("gj_findBtn_001.png"); - m_search = CCMenuItemSpriteExtra::create( - searchSpr, - this, - menu_selector(MoreLeaderboards::onSearch) - ); - m_search->setPosition(239, 104); - m_menu->addChild(m_search); - - auto mod_filter_btn_spr = CCSprite::createWithSpriteFrameName("modBadge_01_001.png"); - mod_filter_btn_spr->setScale(1.4f); - if (!modFilter) { - mod_filter_btn_spr->setColor({ 100, 100, 100 }); - } - mod_filter_btn = CCMenuItemSpriteExtra::create( - mod_filter_btn_spr, - this, - menu_selector(MoreLeaderboards::onModFilter) - ); - mod_filter_btn->setPosition(239.f, 69.f); - m_menu->addChild(mod_filter_btn); - - // Mod type filter - if (modFilter) { - CCSprite* mod_filter_type_spr; - CCSprite* mod_filter_type_spr1; - CCSprite* mod_filter_type_spr2; - CCSprite* mod_filter_type_spr3; - switch (modFilterType) { - case 0: - mod_filter_type_spr1 = CCSprite::createWithSpriteFrameName("modBadge_01_001.png"); - mod_filter_type_spr1->setScale(1.2f); - mod_filter_type_spr2 = CCSprite::createWithSpriteFrameName("modBadge_02_001.png"); - mod_filter_type_spr2->setScale(1.2f); - mod_filter_type_spr3 = CCSprite::createWithSpriteFrameName("modBadge_03_001.png"); - mod_filter_type_spr3->setScale(1.2f); - - mod_filter_type_spr1->setPosition({30, 21}); - mod_filter_type_spr2->setPosition({10, 21}); - mod_filter_type_spr3->setPosition({20, 11}); - - mod_filter_type_spr = CCSprite::create(); - mod_filter_type_spr->addChild(mod_filter_type_spr1); - mod_filter_type_spr->addChild(mod_filter_type_spr2); - mod_filter_type_spr->addChild(mod_filter_type_spr3); - mod_filter_type_spr->setContentSize({ 40, 40 }); - break; - case 1: - mod_filter_type_spr = CCSprite::createWithSpriteFrameName("modBadge_01_001.png"); - mod_filter_type_spr->setScale(1.4f); - break; - case 2: - mod_filter_type_spr = CCSprite::createWithSpriteFrameName("modBadge_02_001.png"); - mod_filter_type_spr->setScale(1.4f); - break; - case 3: - mod_filter_type_spr = CCSprite::createWithSpriteFrameName("modBadge_03_001.png"); - mod_filter_type_spr->setScale(1.4f); - break; - }; - - mod_filter_type_btn = CCMenuItemSpriteExtra::create( - mod_filter_type_spr, - this, - menu_selector(MoreLeaderboards::onModFilterType) - ); - mod_filter_type_btn->setPosition(239.f, 34.f); - m_menu->addChild(mod_filter_type_btn); - } -} - -void MoreLeaderboards::onTabPageLeft(CCObject* pSender) { - if (loading) return; - - tab_page--; - changeTabPage(); - - page = 0; - this->onTab(nullptr); -} - -void MoreLeaderboards::onTabPageRight(CCObject* pSender) { - if (loading) return; - - tab_page++; - changeTabPage(); - - page = 0; - this->onTab(nullptr); -} - -void MoreLeaderboards::onModFilter(CCObject* pSender) { - if (loading) return; - - modFilter = !modFilter; - modFilterType = 0; - - page = 0; - this->onTab(nullptr); -} - -void MoreLeaderboards::onModFilterType(CCObject* pSender) { - if (loading) return; - - if (modFilterType == 3) { - modFilterType = 0; - } else - modFilterType += 1; - - page = 0; - this->onTab(nullptr); -} - -void MoreLeaderboards::changeTabPage() { - if (m_search != nullptr) m_search->removeFromParentAndCleanup(true); - m_search = nullptr; - - if (m_tab1 != nullptr) m_tab1->removeFromParentAndCleanup(true); - if (m_tab2 != nullptr) m_tab2->removeFromParentAndCleanup(true); - if (m_tab3 != nullptr) m_tab3->removeFromParentAndCleanup(true); - if (m_tab4 != nullptr) m_tab4->removeFromParentAndCleanup(true); - if (m_tab5 != nullptr) m_tab5->removeFromParentAndCleanup(true); - if (m_tab6 != nullptr) m_tab6->removeFromParentAndCleanup(true); - - m_tab1 = nullptr; - m_tab2 = nullptr; - m_tab3 = nullptr; - m_tab4 = nullptr; - m_tab5 = nullptr; - m_tab6 = nullptr; - - if (this->getChildByIDRecursive("menu_gdutils_buttons") != nullptr) { - this->getChildByIDRecursive("menu_gdutils_buttons")->removeFromParentAndCleanup(true); - } - - m_menu = CCMenu::create(); - m_menu->setID("menu_gdutils_buttons"); - m_menu->setZOrder(1); - - auto stars_sprite = CCSprite::createWithSpriteFrameName("star_small01_001.png"); - auto moons_sprite = CCSprite::createWithSpriteFrameName("moon_small01_001.png"); - auto diamond_sprite = CCSprite::createWithSpriteFrameName("diamond_small01_001.png"); - auto usercoins_sprite = CCSprite::createWithSpriteFrameName("GJ_coinsIcon2_001.png"); - auto demons_sprite = CCSprite::createWithSpriteFrameName("GJ_demonIcon_001.png"); - auto creators_sprite = CCSprite::createWithSpriteFrameName("GJ_hammerIcon_001.png"); - - auto classic_sprite = CCSprite::createWithSpriteFrameName("star_small01_001.png"); - classic_sprite->setPositionX(30); - classic_sprite->setScale(1.2f); - - auto platformer_sprite = CCSprite::createWithSpriteFrameName("moon_small01_001.png"); - platformer_sprite->setPositionX(30); - platformer_sprite->setScale(1.2f); - - auto better_progression = CCSprite::create("better_progression.png"_spr); - auto pointercrate = CCSprite::createWithSpriteFrameName("diffIcon_06_btn_001.png"); - auto pointercrate_text = CCLabelBMFont::create("PC", "goldFont.fnt"); - pointercrate_text->setPosition(28, 0); - pointercrate_text->setScale(0.5f); - pointercrate->addChild(pointercrate_text); - - auto easydemon_sprite_classic = CCSprite::createWithSpriteFrameName("diffIcon_07_btn_001.png"); - easydemon_sprite_classic->addChild(classic_sprite); - auto mediumdemon_sprite_classic = CCSprite::createWithSpriteFrameName("diffIcon_08_btn_001.png"); - mediumdemon_sprite_classic->addChild(classic_sprite); - auto harddemon_sprite_classic = CCSprite::createWithSpriteFrameName("diffIcon_06_btn_001.png"); - harddemon_sprite_classic->addChild(classic_sprite); - auto insane_sprite_classic = CCSprite::createWithSpriteFrameName("diffIcon_09_btn_001.png"); - insane_sprite_classic->addChild(classic_sprite); - auto extreme_sprite_classic = CCSprite::createWithSpriteFrameName("diffIcon_10_btn_001.png"); - extreme_sprite_classic->addChild(classic_sprite); - - auto easydemon_sprite_platformer = CCSprite::createWithSpriteFrameName("diffIcon_07_btn_001.png"); - easydemon_sprite_platformer->addChild(platformer_sprite); - auto mediumdemon_sprite_platformer = CCSprite::createWithSpriteFrameName("diffIcon_08_btn_001.png"); - mediumdemon_sprite_platformer->addChild(platformer_sprite); - auto harddemon_sprite_platformer = CCSprite::createWithSpriteFrameName("diffIcon_06_btn_001.png"); - harddemon_sprite_platformer->addChild(platformer_sprite); - auto insane_sprite_platformer = CCSprite::createWithSpriteFrameName("diffIcon_09_btn_001.png"); - insane_sprite_platformer->addChild(platformer_sprite); - auto extreme_sprite_platformer = CCSprite::createWithSpriteFrameName("diffIcon_10_btn_001.png"); - extreme_sprite_platformer->addChild(platformer_sprite); - - switch (tab_page) { - case 0: - MoreLeaderboards::g_tab = StatsListType::Stars; - - m_tab1 = NewTabButton::create(TabBaseColor::Unselected, TabBaseColor::Selected, stars_sprite, this, menu_selector(MoreLeaderboards::onTab)); - m_tab1->setPosition(-140.f, 132); - m_tab1->setTag(static_cast(StatsListType::Stars)); - m_tab1->setZOrder(30); - m_tab1->setScale(0.8f); - m_menu->addChild(m_tab1); - - m_tab2 = NewTabButton::create(TabBaseColor::Unselected, TabBaseColor::Selected, moons_sprite, this, menu_selector(MoreLeaderboards::onTab)); - m_tab2->setPosition(-86.f, 132); - m_tab2->setTag(static_cast(StatsListType::Moons)); - m_tab2->setZOrder(30); - m_tab2->setScale(0.8f); - m_menu->addChild(m_tab2); - - m_tab3 = NewTabButton::create(TabBaseColor::Unselected, TabBaseColor::Selected, usercoins_sprite, this, menu_selector(MoreLeaderboards::onTab)); - m_tab3->setPosition(-30.f, 132); - m_tab3->setTag(static_cast(StatsListType::UserCoins)); - m_tab3->setZOrder(30); - m_tab3->setScale(0.8f); - m_menu->addChild(m_tab3); - - m_tab4 = NewTabButton::create(TabBaseColor::Unselected, TabBaseColor::Selected, demons_sprite, this, menu_selector(MoreLeaderboards::onTab)); - m_tab4->setPosition(26.f, 132); - m_tab4->setTag(static_cast(StatsListType::Demons)); - m_tab4->setZOrder(30); - m_tab4->setScale(0.8f); - m_menu->addChild(m_tab4); - - m_tab5 = NewTabButton::create(TabBaseColor::Unselected, TabBaseColor::Selected, diamond_sprite, this, menu_selector(MoreLeaderboards::onTab)); - m_tab5->setPosition(82.f, 132); - m_tab5->setTag(static_cast(StatsListType::Diamonds)); - m_tab5->setZOrder(30); - m_tab5->setScale(0.8f); - m_menu->addChild(m_tab5); - - m_tab6 = NewTabButton::create(TabBaseColor::Unselected, TabBaseColor::Selected, creators_sprite, this, menu_selector(MoreLeaderboards::onTab)); - m_tab6->setPosition(138.f, 132); - m_tab6->setTag(static_cast(StatsListType::Creators)); - m_tab6->setZOrder(30); - m_tab6->setScale(0.8f); - m_menu->addChild(m_tab6); - - break; - case 1: - if (geode::Loader::get()->isModLoaded("itzkiba.better_progression")) { - MoreLeaderboards::g_tab = StatsListType::BetterProgression; - - m_tab1 = NewTabButton::create(TabBaseColor::Unselected, TabBaseColor::Selected, better_progression, this, menu_selector(MoreLeaderboards::onTab)); - m_tab1->setPosition(-140.f, 132); - m_tab1->setTag(static_cast(StatsListType::BetterProgression)); - m_tab1->setZOrder(30); - m_tab1->setScale(0.8f); - m_menu->addChild(m_tab1); - } else { - MoreLeaderboards::g_tab = StatsListType::Pointercrate; - - m_tab1 = NewTabButton::create(TabBaseColor::Unselected, TabBaseColor::Selected, pointercrate, this, menu_selector(MoreLeaderboards::onTab)); - m_tab1->setPosition(-140.f, 132); - m_tab1->setTag(static_cast(StatsListType::Pointercrate)); - m_tab1->setZOrder(30); - m_tab1->setScale(0.8f); - m_menu->addChild(m_tab1); - } - - m_tab2 = NewTabButton::create(TabBaseColor::Unselected, TabBaseColor::Selected, easydemon_sprite_classic, this, menu_selector(MoreLeaderboards::onTab)); - m_tab2->setPosition(-86.f, 132); - m_tab2->setTag(static_cast(StatsListType::classicDemonsEasy)); - m_tab2->setZOrder(30); - m_tab2->setScale(0.8f); - m_menu->addChild(m_tab2); - - m_tab3 = NewTabButton::create(TabBaseColor::Unselected, TabBaseColor::Selected, mediumdemon_sprite_classic, this, menu_selector(MoreLeaderboards::onTab)); - m_tab3->setPosition(-30.f, 132); - m_tab3->setTag(static_cast(StatsListType::classicDemonsMedium)); - m_tab3->setZOrder(30); - m_tab3->setScale(0.8f); - m_menu->addChild(m_tab3); - - m_tab4 = NewTabButton::create(TabBaseColor::Unselected, TabBaseColor::Selected, harddemon_sprite_classic, this, menu_selector(MoreLeaderboards::onTab)); - m_tab4->setPosition(26.f, 132); - m_tab4->setTag(static_cast(StatsListType::classicDemonsHard)); - m_tab4->setZOrder(30); - m_tab4->setScale(0.8f); - m_menu->addChild(m_tab4); - - m_tab5 = NewTabButton::create(TabBaseColor::Unselected, TabBaseColor::Selected, insane_sprite_classic, this, menu_selector(MoreLeaderboards::onTab)); - m_tab5->setPosition(82.f, 132); - m_tab5->setTag(static_cast(StatsListType::classicDemonsInsane)); - m_tab5->setZOrder(30); - m_tab5->setScale(0.8f); - m_menu->addChild(m_tab5); - - m_tab6 = NewTabButton::create(TabBaseColor::Unselected, TabBaseColor::Selected, extreme_sprite_classic, this, menu_selector(MoreLeaderboards::onTab)); - m_tab6->setPosition(138.f, 132); - m_tab6->setTag(static_cast(StatsListType::classicDemonsExtreme)); - m_tab6->setZOrder(30); - m_tab6->setScale(0.8f); - m_menu->addChild(m_tab6); - - break; - case 2: - MoreLeaderboards::g_tab = StatsListType::platformerDemonsEasy; - - m_tab1 = NewTabButton::create(TabBaseColor::Unselected, TabBaseColor::Selected, easydemon_sprite_platformer, this, menu_selector(MoreLeaderboards::onTab)); - m_tab1->setPosition(-140.f, 132); - m_tab1->setTag(static_cast(StatsListType::platformerDemonsEasy)); - m_tab1->setZOrder(30); - m_tab1->setScale(0.8f); - m_menu->addChild(m_tab1); - - m_tab2 = NewTabButton::create(TabBaseColor::Unselected, TabBaseColor::Selected, mediumdemon_sprite_platformer, this, menu_selector(MoreLeaderboards::onTab)); - m_tab2->setPosition(-86.f, 132); - m_tab2->setTag(static_cast(StatsListType::platformerDemonsMedium)); - m_tab2->setZOrder(30); - m_tab2->setScale(0.8f); - m_menu->addChild(m_tab2); - - m_tab3 = NewTabButton::create(TabBaseColor::Unselected, TabBaseColor::Selected, harddemon_sprite_platformer, this, menu_selector(MoreLeaderboards::onTab)); - m_tab3->setPosition(-30.f, 132); - m_tab3->setTag(static_cast(StatsListType::platformerDemonsHard)); - m_tab3->setZOrder(30); - m_tab3->setScale(0.8f); - m_menu->addChild(m_tab3); - - m_tab4 = NewTabButton::create(TabBaseColor::Unselected, TabBaseColor::Selected, insane_sprite_platformer, this, menu_selector(MoreLeaderboards::onTab)); - m_tab4->setPosition(26.f, 132); - m_tab4->setTag(static_cast(StatsListType::platformerDemonsInsane)); - m_tab4->setZOrder(30); - m_tab4->setScale(0.8f); - m_menu->addChild(m_tab4); - - m_tab5 = NewTabButton::create(TabBaseColor::Unselected, TabBaseColor::Selected, extreme_sprite_platformer, this, menu_selector(MoreLeaderboards::onTab)); - m_tab5->setPosition(82.f, 132); - m_tab5->setTag(static_cast(StatsListType::platformerDemonsExtreme)); - m_tab5->setZOrder(30); - m_tab5->setScale(0.8f); - m_menu->addChild(m_tab5); - - if (geode::Loader::get()->isModLoaded("itzkiba.better_progression")) { - m_tab6 = NewTabButton::create(TabBaseColor::Unselected, TabBaseColor::Selected, pointercrate, this, menu_selector(MoreLeaderboards::onTab)); - m_tab6->setPosition(138.f, 132); - m_tab6->setTag(static_cast(StatsListType::Pointercrate)); - m_tab6->setZOrder(30); - m_tab6->setScale(0.8f); - m_menu->addChild(m_tab6); - } - - break; - } - - this->addChild(m_menu); -} - -void MoreLeaderboards::onPageLeft(CCObject* pSender) { - if (loading) return; - - MoreLeaderboards::username = ""; - MoreLeaderboards::scroll_int = 0; - - loading = true; - - loading_circle = LoadingCircle::create(); - loading_circle->setParentLayer(this); - loading_circle->setZOrder(25); - loading_circle->show(); - - if (displayedData) { - displayedData->release(); - displayedData = cocos2d::CCArray::create(); - displayedData->retain(); - } - - page--; - resetInfos(); - - if (loading) { - startLoadingMore(); - loadPageMore(); - } -} - -void MoreLeaderboards::onPageRight(CCObject* pSender) { - if (loading) return; - - MoreLeaderboards::username = ""; - MoreLeaderboards::scroll_int = 0; - - loading = true; - - loading_circle = LoadingCircle::create(); - loading_circle->setZOrder(25); - loading_circle->setParentLayer(this); - loading_circle->show(); - - if (displayedData) { - displayedData->release(); - displayedData = cocos2d::CCArray::create(); - displayedData->retain(); - } - - page++; - resetInfos(); - - if (loading) { - startLoadingMore(); - loadPageMore(); - } -} - -void MoreLeaderboards::resetInfos() { - if (page_label != nullptr) page_label->removeFromParentAndCleanup(true); - if (page_left != nullptr) page_left->removeFromParentAndCleanup(true); - if (page_right != nullptr) page_right->removeFromParentAndCleanup(true); - if (trophy != nullptr) trophy->removeFromParentAndCleanup(true); - if (mod_filter_btn != nullptr) mod_filter_btn->removeFromParentAndCleanup(true); - if (mod_filter_type_btn != nullptr) mod_filter_type_btn->removeFromParentAndCleanup(true); - - page_label = nullptr; - page_left = nullptr; - page_right = nullptr; - trophy = nullptr; - mod_filter_btn = nullptr; - mod_filter_type_btn = nullptr; -} - -void MoreLeaderboards::onTab(CCObject* pSender) { - if (loading) return; - - loading = true; - - loading_circle = LoadingCircle::create(); - loading_circle->setZOrder(25); - loading_circle->setParentLayer(this); - loading_circle->show(); - - if (displayedData) { - displayedData->release(); - displayedData = cocos2d::CCArray::create(); - displayedData->retain(); - } - - if (pSender) { - MoreLeaderboards::g_tab = static_cast(pSender->getTag()); - } - resetInfos(); - - page = 0; - - auto toggleTab = [this](CCMenuItemToggler* member) -> void { - auto isSelected = member->getTag() == static_cast(MoreLeaderboards::g_tab); - auto targetMenu = m_menu; - member->toggle(isSelected); - if (member->getParent() != targetMenu) { - member->retain(); - member->removeFromParent(); - targetMenu->addChild(member); - member->release(); - } - if (isSelected && m_tabsGradientStencil) - m_tabsGradientStencil->setPosition(member->m_onButton->convertToWorldSpace({0.f, 0.f})); - }; - - if (m_tab1 != nullptr) toggleTab(m_tab1); - if (m_tab2 != nullptr) toggleTab(m_tab2); - if (m_tab3 != nullptr) toggleTab(m_tab3); - if (m_tab4 != nullptr) toggleTab(m_tab4); - if (m_tab5 != nullptr) toggleTab(m_tab5); - if (m_tab6 != nullptr) toggleTab(m_tab6); - - if (loading) { - startLoadingMore(); - loadPageMore(); - } -} - -class $modify(LeaderboardsLayer) { - bool init(LeaderboardState state) { - if (!LeaderboardsLayer::init(state)) return false; - auto menu = CCMenu::create(); - - auto plusSpr = CCSprite::createWithSpriteFrameName("GJ_plusBtn_001.png"); - plusSpr->setScale(.8f); - auto plusBtn = CCMenuItemSpriteExtra::create( - plusSpr, - this, - menu_selector(MoreLeaderboards::onMoreLeaderboards) - ); - plusBtn->setPosition(239, 20); - menu->addChild(plusBtn); - - auto modsSpr = CCSprite::createWithSpriteFrameName("modBadge_01_001.png"); - modsSpr->setScale(1.5f); - auto modsBtn = CCMenuItemSpriteExtra::create( - modsSpr, - this, - menu_selector(MoreLeaderboards::onModsList) - ); - modsBtn->setPosition(239, -24); - menu->addChild(modsBtn); - - this->addChild(menu); - - return true; - } -}; diff --git a/src/MoreLeaderboards/MoreLeaderboards.h b/src/MoreLeaderboards/MoreLeaderboards.h deleted file mode 100644 index ead95df..0000000 --- a/src/MoreLeaderboards/MoreLeaderboards.h +++ /dev/null @@ -1,120 +0,0 @@ -#pragma once -#include "../includes.h" -#include -#include "MoreLeaderboardsListView.h" -#include "SelectRegion.h" - -#include -#include - -enum class StatsListType { - Stars, - Diamonds, - UserCoins, - Demons, - Moons, - Creators, - classicDemonsEasy, - classicDemonsMedium, - classicDemonsHard, - classicDemonsInsane, - classicDemonsExtreme, - platformerDemonsEasy, - platformerDemonsMedium, - platformerDemonsHard, - platformerDemonsInsane, - platformerDemonsExtreme, - BetterProgression, - Pointercrate -}; - -class MoreLeaderboards : public CCLayer { - MoreLeaderboardsListView* leaderboardView = nullptr; - CustomListView* leaderboardViewScore = nullptr; - GJListLayer* listLayer = nullptr; - LoadingCircle* loading_circle = nullptr; - cocos2d::CCArray* displayedData; - - CCLabelBMFont* page_label = nullptr; - CCMenuItemSpriteExtra* page_left = nullptr; - CCMenuItemSpriteExtra* page_right = nullptr; - CCMenuItemSpriteExtra* tab_page_left = nullptr; - CCMenuItemSpriteExtra* tab_page_right = nullptr; - CCMenuItemSpriteExtra* mod_filter_btn = nullptr; - CCMenuItemSpriteExtra* mod_filter_type_btn = nullptr; - CCSprite* trophy = nullptr; - - CCString* datas = nullptr; - std::string data_region = ""; - - std::string country_id = "NAN"; - - CCMenu* m_menu; - CCMenuItemToggler* m_tab1; - CCMenuItemToggler* m_tab2; - CCMenuItemToggler* m_tab3; - CCMenuItemToggler* m_tab4; - CCMenuItemToggler* m_tab5; - CCMenuItemToggler* m_tab6; - int tab_page = 0; - - CCMenuItemSpriteExtra* m_search; - - #ifndef GEODE_IS_IOS - CCClippingNode* m_tabsGradientNode = nullptr; - #else - CCNode* m_tabsGradientNode = nullptr; - #endif - CCSprite* m_tabsGradientSprite = nullptr; - CCSprite* m_tabsGradientStencil = nullptr; - - protected: - virtual bool init(std::string type); - virtual void keyBackClicked(); - void startLoadingMods(); - void loadPageMods(); - void startLoadingMore(); - void loadPageMore(); - void loadPageStats(); - - void resetInfos(); - public: - static std::string data_response_moreLB; - static StatsListType g_tab; - static std::string username; - static int scroll_int; - static bool modFilter; - static int modFilterType; - bool loading = false; - - EventListener m_listener; - - void onMoreLeaderboards(CCObject*); - void onModsList(CCObject*); - void onRegion(CCObject*); - void onInfo(CCObject*); - void onLBInfo(CCObject*); - - void onTab(CCObject* pSender); - void onPageLeft(CCObject* pSender); - void onPageRight(CCObject* pSender); - - void loadTabPageButtons(); - void onTabPageLeft(CCObject* pSender); - void onTabPageRight(CCObject* pSender); - void onModFilter(CCObject* pSender); - void onModFilterType(CCObject* pSender); - void changeTabPage(); - - void onSearch(CCObject*); - - static MoreLeaderboards* create(std::string type); - void fadeLoadingCircle(); - void handle_request_mods(std::string const& data); - void handle_request_more(std::string const& data); - - static std::vector getWords(std::string s, std::string delim); - static CCDictionary* responseToDict(const std::string& response); - - void backButton(CCObject*); -}; diff --git a/src/MoreLeaderboards/MoreLeaderboardsCell.cpp b/src/MoreLeaderboards/MoreLeaderboardsCell.cpp deleted file mode 100644 index 160b659..0000000 --- a/src/MoreLeaderboards/MoreLeaderboardsCell.cpp +++ /dev/null @@ -1,162 +0,0 @@ -#include "MoreLeaderboardsCell.h" - -void MoreLeaderboardsCell::loadFromDict(cocos2d::CCDictionary* content) -{ - auto winSize = CCDirector::sharedDirector()->getWinSize(); - auto name = content->valueForKey("modstring")->getCString(); - - auto mediaMenu = CCMenu::create(); - float mediaXPosition = 327; - int mediaCount = 0; - - std::string usernameTemp = ""; - - std::vector data = MoreLeaderboards::getWords(name, "?"); - std::vector data2 = data; - - while (data.size() > 0) { - std::string id = data[0]; - std::string name = data[1]; - - if (id == "1") { - usernameTemp = name; - } else if (id == "3") { - if (name == "0") { - auto robtopbadge = cocos2d::CCSprite::createWithSpriteFrameName("robtop_badge.png"_spr); - robtopbadge->setPosition({ 30, 20 }); - robtopbadge->setScale(1.225f); - this->m_mainLayer->addChild(robtopbadge); - } else if (name == "2") { - auto elderModBadge = cocos2d::CCSprite::createWithSpriteFrameName("modBadge_01_001.png"); - elderModBadge->setPosition({ 30, 20 }); - elderModBadge->setScale(1.225f); - this->m_mainLayer->addChild(elderModBadge); - } else if (name == "1") { - auto modBadge = cocos2d::CCSprite::createWithSpriteFrameName("modBadge_02_001.png"); - modBadge->setPosition({ 30, 20 }); - modBadge->setScale(1.225f); - this->m_mainLayer->addChild(modBadge); - } else if (name == "3") { - auto modBadge = cocos2d::CCSprite::createWithSpriteFrameName("modBadge_03_001.png"); - modBadge->setPosition({ 30, 20 }); - modBadge->setScale(1.225f); - this->m_mainLayer->addChild(modBadge); - } - } else if (id == "4") { - if (name != "NULL") { - auto discordSpr = CCSprite::createWithSpriteFrameName("gj_discordIcon_001.png"); - auto discordBtn = CCMenuItemSpriteExtra::create( - discordSpr, - this, - menu_selector(MoreLeaderboardsCell::callback_link) - ); - auto discord_link = CCString::create(name); - discordBtn->setUserObject(discord_link); - mediaMenu->addChild(discordBtn); - mediaCount++; - } - } else if (id == "5") { - if (name != "NULL") { - auto twitchSpr = CCSprite::createWithSpriteFrameName("gj_twitchIcon_001.png"); - auto twitchBtn = CCMenuItemSpriteExtra::create( - twitchSpr, - this, - menu_selector(MoreLeaderboardsCell::callback_link) - ); - auto twitch_link = CCString::create(name); - twitchBtn->setUserObject(twitch_link); - mediaMenu->addChild(twitchBtn); - mediaCount++; - } - } else if (id == "6") { - if (name != "NULL") { - auto youtubeSpr = CCSprite::createWithSpriteFrameName("gj_ytIcon_001.png"); - auto youtubeBtn = CCMenuItemSpriteExtra::create( - youtubeSpr, - this, - menu_selector(MoreLeaderboardsCell::callback_link) - ); - auto youtube_link = CCString::create(name); - youtubeBtn->setUserObject(youtube_link); - mediaMenu->addChild(youtubeBtn); - mediaCount++; - } - } - - data.erase(data.begin()); - data.erase(data.begin()); - } - - auto username = cocos2d::CCLabelBMFont::create(usernameTemp.c_str(), "goldFont.fnt"); - username->setPosition({ 57, 20 }); - username->setScale(.725f); - - auto dictData = CCDictionary::create(); - dictData->setObject(CCString::create(data2[3]), "accountID"); - dictData->setObject(CCString::create(data2[13]), "playerID"); - dictData->setObject(CCString::create(usernameTemp.c_str()), "username"); - - auto usernameBtn = CCMenuItemSpriteExtra::create( - username, - this, - menu_selector(MoreLeaderboardsCell::callback_user) - ); - usernameBtn->setPosition({0, 0}); - usernameBtn->setAnchorPoint({0, 0.5}); - usernameBtn->setUserObject(dictData); - - auto menu = CCMenu::create(); - menu->addChild(usernameBtn); - menu->setPosition({ 55, 20 }); - - this->m_mainLayer->addChild(menu); - - if (mediaCount == 1) { - mediaXPosition = 327; - } else if (mediaCount == 2) { - mediaXPosition = 308; - } else if (mediaCount == 3) { - mediaXPosition = 289; - } - - mediaMenu->alignItemsHorizontallyWithPadding(5); - mediaMenu->setAnchorPoint({1, 0.5}); - mediaMenu->setPosition({ mediaXPosition, 20 }); - this->m_mainLayer->addChild(mediaMenu); - - this->m_mainLayer->setVisible(true); - this->m_backgroundLayer->setOpacity(255); -}; - -MoreLeaderboardsCell::MoreLeaderboardsCell(const char* name, CCSize size) : - GenericListCell(name, size) {} - -MoreLeaderboardsCell* MoreLeaderboardsCell::create(const char* key, CCSize size) { - auto ret = new MoreLeaderboardsCell(key, size); - if (ret && ret->init()) { - ret->autorelease(); - } else { - delete ret; - ret = nullptr; - } - return ret; -} - -void MoreLeaderboardsCell::callback_user(CCObject* pSender) { - GameLevelManager* glm = GameLevelManager::sharedState(); - - auto data = static_cast(static_cast(pSender)->getUserObject()); - auto accountID = numFromString(data->valueForKey("accountID")->getCString()).unwrapOrDefault(); - auto playerID = numFromString(data->valueForKey("playerID")->getCString()).unwrapOrDefault(); - auto username = data->valueForKey("username")->getCString(); - - - glm->storeUserName(playerID, accountID, username); - - ProfilePage::create(accountID, true)->show(); -} - -void MoreLeaderboardsCell::callback_link(CCObject* pSender) { - auto data = static_cast(static_cast(pSender)->getUserObject()); - web::openLinkInBrowser(data->getCString()); -} diff --git a/src/MoreLeaderboards/MoreLeaderboardsCell.h b/src/MoreLeaderboards/MoreLeaderboardsCell.h deleted file mode 100644 index 7a8a1d0..0000000 --- a/src/MoreLeaderboards/MoreLeaderboardsCell.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once -#include "MoreLeaderboards.h" -#include - -class MoreLeaderboardsCell : public GenericListCell { -public: - inline static CCDictionary* static_content; - inline static CCString* static_data_other; - - MoreLeaderboardsCell(const char* name, CCSize size); - void loadFromDict(CCDictionary* dict); - void callback_user(CCObject* pSender); - void callback_link(CCObject* pSender); - static MoreLeaderboardsCell* create(const char* key, CCSize size); -}; diff --git a/src/MoreLeaderboards/MoreLeaderboardsListView.cpp b/src/MoreLeaderboards/MoreLeaderboardsListView.cpp deleted file mode 100644 index 1de6a55..0000000 --- a/src/MoreLeaderboards/MoreLeaderboardsListView.cpp +++ /dev/null @@ -1,75 +0,0 @@ -#include "MoreLeaderboardsListView.h" -#include "MoreLeaderboards.h" -#include "MoreLeaderboardsCell.h" -#include "../includes.h" - -MoreLeaderboardsListView* MoreLeaderboardsListView::create(cocos2d::CCArray* levels, float width, float height) { - auto ret = new MoreLeaderboardsListView(); - if (ret && ret->init(levels, static_cast(0x420), width, height)) { - ret->autorelease(); - } else { - delete ret; - ret = nullptr; - } - return ret; -}; - -void MoreLeaderboardsListView::setupList(float d) { - this->m_itemSeparation = 40.0f; - - this->m_tableView->reloadData(); - - auto coverage = calculateNodeCoverage(m_tableView->m_contentLayer->getChildren()); - if (this->m_entries->count() > 4) - m_tableView->m_contentLayer->setContentSize({-coverage.origin.x + coverage.size.width, -coverage.origin.y + coverage.size.height}); - - this->m_tableView->moveToTop(); - - if (this->m_entries->count() == 1) - this->m_tableView->moveToTopWithOffset(this->m_itemSeparation); -}; - -cocos2d::CCRect MoreLeaderboardsListView::calculateNodeCoverage(CCArray* nodes){ - CCRect coverage; - CCObject* childObj; - CCARRAY_FOREACH(nodes, childObj) { - if (misc::getNodeName(childObj) != "cocos2d::CCNode" && misc::getNodeName(childObj) != "MoreLeaderboardsCell") continue; - CCNode* child = static_cast(childObj); // android doesnt like dynamic_cast - if(child == nullptr) continue; - auto contentSize = child->getScaledContentSize(); - contentSize.width *= child->getAnchorPoint().x; - contentSize.height *= child->getAnchorPoint().y; - auto pos = child->getPosition() - contentSize; - - contentSize = child->getScaledContentSize(); - auto contentPoint = CCPoint { 1.f, 1.f } - child->getAnchorPoint(); - contentSize.width *= contentPoint.x; - contentSize.height *= contentPoint.y; - - auto csize = child->getPosition() + contentSize; - if (pos.x < coverage.origin.x) { - coverage.origin.x = pos.x; - } - if (pos.y < coverage.origin.y) { - coverage.origin.y = pos.y; - } - if (csize.x > coverage.size.width) { - coverage.size.width = csize.x; - } - if (csize.y > coverage.size.height) { - coverage.size.height = csize.y; - } - } - return coverage; -}; - -void MoreLeaderboardsListView::loadCell(TableViewCell* cell, int index) { - static_cast(cell)->loadFromDict( - static_cast(this->m_entries->objectAtIndex(index)) - ); - static_cast(cell)->updateBGColor(index); -} - -TableViewCell* MoreLeaderboardsListView::getListCell(const char* key) { - return new MoreLeaderboardsCell(key, { this->m_width, 40.0f }); -}; diff --git a/src/MoreLeaderboards/MoreLeaderboardsListView.h b/src/MoreLeaderboards/MoreLeaderboardsListView.h deleted file mode 100644 index dfea057..0000000 --- a/src/MoreLeaderboards/MoreLeaderboardsListView.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once -#include - -using namespace geode::prelude; - -class MoreLeaderboardsListView : public CustomListView { - protected: - void setupList(float) override; - TableViewCell* getListCell(const char* key) override; - void loadCell(TableViewCell* cell, int index) override; - static CCRect calculateNodeCoverage(CCArray* nodes); - public: - static MoreLeaderboardsListView* create(CCArray* levels, float width, float height); -}; diff --git a/src/MoreLeaderboards/Newtab.cpp b/src/MoreLeaderboards/Newtab.cpp deleted file mode 100644 index 2a4b0db..0000000 --- a/src/MoreLeaderboards/Newtab.cpp +++ /dev/null @@ -1,118 +0,0 @@ -#include "Newtab.h" - -NewTabButton* NewTabButton::create( - TabBaseColor unselected, TabBaseColor selected, CCNode* icon, cocos2d::CCObject* target, - cocos2d::SEL_MenuHandler callback -) { - auto ret = new NewTabButton(); - auto sprOff = NewTabButtonSprite::create(icon, unselected); - auto sprOn = NewTabButtonSprite::create(icon, selected); - if (ret && ret->init(sprOff, sprOn, target, callback)) { - ret->m_offButton->m_colorDip = .3f; - ret->m_offButton->m_colorEnabled = true; - ret->m_offButton->m_scaleMultiplier = 1.f; - ret->m_onButton->setEnabled(false); - ret->autorelease(); - return ret; - } - CC_SAFE_DELETE(ret); - return nullptr; -} - -CCSize NewBasedButtonSprite::getMaxTopSize() const { - return m_obContentSize - CCSize(18.f, 18.f); -} - -void NewBasedButtonSprite::setTopOffset(CCPoint const& offset) { - m_topOffset = offset; - if (m_onTop) { - m_onTop->setPosition(this->getContentSize() / 2 + offset); - } -} -void NewBasedButtonSprite::setTopRelativeScale(float scale) { - m_onTopRelativeScale = scale; - if (m_onTop) { - limitNodeSize(m_onTop, this->getMaxTopSize(), 999.f, .1f); - m_onTop->setScale(m_onTop->getScale() * m_onTopRelativeScale); - } -} - -bool NewBasedButtonSprite::initWithSprite( - char const* sprName, float sprScale, BaseType type, int size, int color -) { - auto spr = CCSprite::create(sprName); - if (!spr) return false; - m_onTopRelativeScale = sprScale; - return this->init(spr, type, size, color); -} - -bool NewBasedButtonSprite::initWithSpriteFrameName( - char const* sprName, float sprScale, BaseType type, int size, int color -) { - auto spr = CCSprite::createWithSpriteFrameName(sprName); - if (!spr) return false; - m_onTopRelativeScale = sprScale; - return this->init(spr, type, size, color); -} - -CCNode* NewBasedButtonSprite::getTopNode() const { - return m_onTop; -} - -NewBasedButtonSprite::~NewBasedButtonSprite() {} - -NewTabButtonSprite* NewTabButtonSprite::create(CCNode* icon, TabBaseColor color, TabBaseSize size) { - auto ret = new NewTabButtonSprite(); - if (ret && ret->init( - icon, BaseType::Tab, - static_cast(size), static_cast(color) - )) { - ret->autorelease(); - return ret; - } - CC_SAFE_DELETE(ret); - return nullptr; -} - -static std::string baseEnumsToString(BaseType type, int size, int color) { -#define ENUMS_TO_STRING(ty_) \ - case BaseType::ty_: { \ - sizeStr = baseEnumToString(static_cast(size));\ - colorStr = baseEnumToString(static_cast(color));\ - } break - - const char* typeStr = baseEnumToString(type); - const char* sizeStr; - const char* colorStr; - switch (type) { - ENUMS_TO_STRING(Circle); - ENUMS_TO_STRING(Cross); - ENUMS_TO_STRING(Account); - ENUMS_TO_STRING(IconSelect); - ENUMS_TO_STRING(Leaderboard); - ENUMS_TO_STRING(Editor); - ENUMS_TO_STRING(Tab); - ENUMS_TO_STRING(Category); - } - return fmt::format("newbase{}_{}_{}.png", typeStr, sizeStr, colorStr); -} - -bool NewBasedButtonSprite::init(CCNode* ontop, BaseType type, int size, int color) { - if (!CCSprite::initWithFile( - Mod::get()->expandSpriteName(baseEnumsToString(type, size, color)).data() - )) return false; - - m_type = type; - m_size = size; - m_color = color; - - if (ontop) { - m_onTop = ontop; - m_onTop->setPosition(this->getContentSize() / 2 + m_topOffset); - limitNodeSize(m_onTop, this->getMaxTopSize(), 999.f, .1f); - m_onTop->setScale(m_onTop->getScale() * m_onTopRelativeScale); - this->addChild(m_onTop); - } - - return true; -} \ No newline at end of file diff --git a/src/MoreLeaderboards/Newtab.h b/src/MoreLeaderboards/Newtab.h deleted file mode 100644 index b5e4c31..0000000 --- a/src/MoreLeaderboards/Newtab.h +++ /dev/null @@ -1,67 +0,0 @@ -#pragma once -#include "../includes.h" - -class NewTabButton : public CCMenuItemToggler { - public: - static NewTabButton* create( - TabBaseColor unselected, TabBaseColor selected, CCNode* icon, - cocos2d::CCObject* target, cocos2d::SEL_MenuHandler callback - ); -}; - -class NewBasedButtonSprite : public cocos2d::CCSprite { - protected: - BaseType m_type; - int m_size; - int m_color; - cocos2d::CCNode* m_onTop = nullptr; - float m_onTopRelativeScale = 1.3f; - cocos2d::CCPoint m_topOffset = cocos2d::CCPointZero; - - bool init(cocos2d::CCNode* ontop, BaseType type, int size, int color); - bool initWithSprite( - const char* sprName, float sprScale, BaseType type, int size, int color - ); - bool initWithSpriteFrameName( - const char* sprName, float sprScale, BaseType type, int size, int color - ); - - virtual cocos2d::CCSize getMaxTopSize() const; - - virtual ~NewBasedButtonSprite(); - - public: - /** - * Create a BasedButtonSprite. In general, it is recommended to use - * the specialized subclasses, such as CircleButtonSprite or - * TabButtonSprite - * @param ontop The top node of the sprite - * @param type The type of the base sprite - * @param size The size of the base sprite. Cast this from the base - * type's respective BaseSize enum - * @param color The color of the base sprite. Cast this from the base - * type's respective BaseColor enum - * @returns Created BasedButtonSprite, or nullptr if parameters were - * invalid - */ - static NewBasedButtonSprite* create( - cocos2d::CCNode* ontop, BaseType type, int size, int color - ); - - /** - * Set an offset to the top sprite - */ - void setTopOffset(cocos2d::CCPoint const& offset); - void setTopRelativeScale(float scale); - - cocos2d::CCNode* getTopNode() const; -}; - -class NewTabButtonSprite : public NewBasedButtonSprite { - public: - static NewTabButtonSprite* create( - CCNode* icon, - TabBaseColor color, - TabBaseSize size = TabBaseSize::Normal - ); -}; \ No newline at end of file diff --git a/src/MoreLeaderboards/SelectRegion.cpp b/src/MoreLeaderboards/SelectRegion.cpp deleted file mode 100644 index 8183d85..0000000 --- a/src/MoreLeaderboards/SelectRegion.cpp +++ /dev/null @@ -1,65 +0,0 @@ -#include "SelectRegion.h" - -static std::unordered_map RUNNING_REQUESTS {}; -std::vector SelectRegion::displayedData = {}; - -std::vector SelectRegion::getWords(std::string s, std::string delim) { - std::vector res; - std::string token = ""; - for (int i = 0; i < s.size(); i++) { - bool flag = true; - for (int j = 0; j < delim.size(); j++) { - if (s[i + j] != delim[j]) flag = false; - } - if (flag) { - if (token.size() > 0) { - res.push_back(token); - token = ""; - i += delim.size() - 1; - } - } else { - token += s[i]; - } - } - res.push_back(token); - return res; -} - -void SelectRegion::scene(const std::function& callback) { - auto popup = new SelectRegion(callback); - - if (popup && popup->initAnchored(250.0f, 210.0f)) { - popup->autorelease(); - CCDirector::sharedDirector()->getRunningScene()->addChild(popup); - } else { - CC_SAFE_DELETE(popup); - } -}; - -bool SelectRegion::setup() { - auto winSize = cocos2d::CCDirector::sharedDirector()->getWinSize(); - auto director = cocos2d::CCDirector::sharedDirector(); - - setTouchEnabled(true); - setKeypadEnabled(true); - - SelectRegion::loadPage(); - return true; -} - -void SelectRegion::loadPage() { - CCArray* data = CCArray::create(); - - for (std::string const& mod : SelectRegion::displayedData) { - auto cell = SelectRegionCell::create(mod, [this](std::string id) { - selectedRegion(id); - removeFromParent(); - }); - data->addObject(cell); - } - - auto border = OldBorder::create(ListView::create(data, 30, 218, 178), { 0xBF, 0x72, 0x3E, 0xFF }, {220, 180}, {1, 1}); - border->setPosition(m_mainLayer->getContentSize() / 2 - border->getContentSize() / 2); - setTouchPriority(100); - m_mainLayer->addChild(border); -} diff --git a/src/MoreLeaderboards/SelectRegion.h b/src/MoreLeaderboards/SelectRegion.h deleted file mode 100644 index 84c5873..0000000 --- a/src/MoreLeaderboards/SelectRegion.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once -#include "../includes.h" -#include -#include "SelectRegionCell.h" -#include "../OldBorder.hpp" - -class SelectRegion : public Popup<> { - LoadingCircle* loading_circle; - std::function selectedRegion; - - protected: - bool setup() override; - void loadPage(); - - cocos2d::CCSize m_sScrLayerSize; - public: - static std::vector displayedData; - - SelectRegion(const std::function& callback) : selectedRegion(callback) {} - - static void scene(const std::function& callback); - - static std::vector getWords(std::string s, std::string delim); -}; \ No newline at end of file diff --git a/src/MoreLeaderboards/SelectRegionCell.cpp b/src/MoreLeaderboards/SelectRegionCell.cpp deleted file mode 100644 index bf555fc..0000000 --- a/src/MoreLeaderboards/SelectRegionCell.cpp +++ /dev/null @@ -1,93 +0,0 @@ -#include "SelectRegionCell.h" - -std::string SelectRegionCell::country_id = "NAN"; - -bool SelectRegionCell::init(std::string mod) { - std::vector data = SelectRegion::getWords(mod, ":"); - std::vector data2 = data; - - while (data.size() > 0) { - std::string id = data[0]; - std::string name = data[1]; - - // sprite - if (id == "1") { - auto username = cocos2d::CCLabelBMFont::create(name.c_str(), "bigFont.fnt"); - username->setPosition({ 38, 21 }); - username->setAnchorPoint({ 0, 0.5 }); - username->limitLabelWidth(100, .550f, .2f); - addChild(username); - } else if (id == "3") { - if (name != "NAN") { - std::string spriteName(name); - std::transform(spriteName.begin(), spriteName.end(), spriteName.begin(), ::tolower); - if (auto flagSpr = CCSprite::createWithSpriteFrameName((""_spr+spriteName+".png").c_str())) { - flagSpr->setPosition({20, 15}); - flagSpr->setScaleY(30 / flagSpr->getContentHeight() * 0.50); - flagSpr->setScaleX(std::min(1.0f, 40 / flagSpr->getContentWidth())); - addChild(flagSpr); - } - } else { - auto earthSpr = CCSprite::create("earth_btn.png"_spr); - earthSpr->setScale(.425f); - earthSpr->setPosition({ 20, 14 }); - addChild(earthSpr); - } - - id_ctr = name; - } else if (id == "5") { - auto nbPlayers = cocos2d::CCLabelBMFont::create((name + " players").c_str(), "goldFont.fnt"); - nbPlayers->setPosition({ 38, 8 }); - nbPlayers->setAnchorPoint({ 0, 0.5 }); - nbPlayers->setScale(.375f); - addChild(nbPlayers); - } - - data.erase(data.begin()); - data.erase(data.begin()); - } - - CCMenu* btnMenu = CCMenu::create(); - btnMenu->setPosition(0, 0); - - if (id_ctr != country_id) { - auto viewSpr = cocos2d::CCSprite::createWithSpriteFrameName("GJ_checkOff_001.png"); - viewSpr->setScale(.575f); - auto viewBtn = CCMenuItemSpriteExtra::create( - viewSpr, - this, - menu_selector(SelectRegionCell::onClick) - ); - viewBtn->setPosition(196, 15); - btnMenu->addChild(viewBtn); - } else { - auto viewSpr = cocos2d::CCSprite::createWithSpriteFrameName("GJ_checkOn_001.png"); - viewSpr->setScale(.575f); - auto viewBtn = CCMenuItemSpriteExtra::create( - viewSpr, - this, - menu_selector(SelectRegionCell::onClick) - ); - viewBtn->setPosition(196, 15); - btnMenu->addChild(viewBtn); - } - - addChild(btnMenu); - - return true; -}; - -void SelectRegionCell::onClick(cocos2d::CCObject* pSender) { - selectedRegion(country_id = id_ctr); -} - -SelectRegionCell* SelectRegionCell::create(std::string mod, const std::function& callback) { - auto ret = new SelectRegionCell(callback); - if (ret && ret->init(mod)) { - ret->retain(); - } else { - delete ret; - ret = nullptr; - } - return ret; -} diff --git a/src/MoreLeaderboards/SelectRegionCell.h b/src/MoreLeaderboards/SelectRegionCell.h deleted file mode 100644 index e1bec07..0000000 --- a/src/MoreLeaderboards/SelectRegionCell.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once -#include "SelectRegion.h" -#include - -class SelectRegionCell : public cocos2d::CCLayer { - std::function selectedRegion; - std::string id_ctr; - - void onClick(cocos2d::CCObject* pSender); -public: - SelectRegionCell(const std::function& callback) : selectedRegion(callback) {} - - static std::string country_id; - bool init(std::string mod); - static SelectRegionCell* create(std::string mod, const std::function& callback); -}; \ No newline at end of file diff --git a/src/Notifications/DailyChest.cpp b/src/Notifications/DailyChest.cpp deleted file mode 100644 index 5a968c9..0000000 --- a/src/Notifications/DailyChest.cpp +++ /dev/null @@ -1,50 +0,0 @@ -#include "DailyChest.h" -#include "EventsPush.h" - -static GJRewardItem* time1 = nullptr; -static GJRewardItem* time2 = nullptr; - -void DailyChest::getRewards(unsigned int type) { - auto glm = GameLevelManager::sharedState(); - if (glm) { - glm->m_GJRewardDelegate = this; - glm->getGJRewards(0); - } -}; - -void DailyChest::rewardsStatusFinished(int p0) { - GameStatsManager* gsm; - // Check if rewards are loaded - gsm = GameStatsManager::sharedState(); - if (gsm->m_rewardItems->count() == 0) return; - - // Get the rewards - int time1_value = 0x1; - int time2_value = 0x2; - time1 = typeinfo_cast(gsm->m_rewardItems->objectForKey(time1_value)); - time2 = typeinfo_cast(gsm->m_rewardItems->objectForKey(time2_value)); - - EventData data; - data.sprite = "GJ_square04.png"; - data.level_name = " "; - data.level_creator = " "; - if (time1 != nullptr) { - if (time1->m_timeRemaining == 0) { - data.type = EventType::smallChest; - data.title = "Small Daily Chest available!"; - EventsPush::pushRateLevel(CCDirector::sharedDirector()->getRunningScene(), data); - } - } - - if (time2 != nullptr) { - if (time2->m_timeRemaining == 0) { - data.type = EventType::largeChest; - data.title = "Big Daily Chest available!"; - EventsPush::pushRateLevel(CCDirector::sharedDirector()->getRunningScene(), data); - } - } -}; - -void DailyChest::rewardsStatusFailed() { - log::error("Failed to get rewards"); -}; diff --git a/src/Notifications/DailyChest.h b/src/Notifications/DailyChest.h deleted file mode 100644 index 964b9c7..0000000 --- a/src/Notifications/DailyChest.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include -using namespace geode::prelude; - -class DailyChest : public CCNode, public GJRewardDelegate { - public: - void getRewards(unsigned int type); - void rewardsStatusFinished(int p0) override; - void rewardsStatusFailed() override; -}; diff --git a/src/Notifications/EventsPush.cpp b/src/Notifications/EventsPush.cpp deleted file mode 100644 index 07df09f..0000000 --- a/src/Notifications/EventsPush.cpp +++ /dev/null @@ -1,916 +0,0 @@ -#include "EventsPush.h" -#include "../Settings/CustomSettings.hpp" -#include "DailyChest.h" -#include "Geode/utils/general.hpp" -#include -#include -#include - -static std::unordered_map RUNNING_REQUESTS {}; -static std::mutex lock_var; -const std::lock_guard lock(lock_var); - -std::vector EventsPush::getWords(std::string s, std::string delim) { - std::vector res; - std::string token = ""; - for (int i = 0; i < s.size(); i++) { - bool flag = true; - for (int j = 0; j < delim.size(); j++) { - if (s[i + j] != delim[j]) flag = false; - } - if (flag) { - if (token.size() > 0) { - res.push_back(token); - token = ""; - i += delim.size() - 1; - } - } else { - token += s[i]; - } - } - res.push_back(token); - return res; -} - -std::queue eventQueue; -bool processingEvents = false; -int zOrder = 200; -EventsPush* EventsPush::create(EventData data) { - auto ret = new EventsPush(); - if (ret && ret->init(data)) { - ret->autorelease(); - return ret; - } - CC_SAFE_DELETE(ret); - return nullptr; -} - -const char* getDemonDifficultyIcon(int stars) { - switch(stars){ - case 3: - return "difficulty_07_btn_001.png"; - case 4: - return "difficulty_08_btn_001.png"; - case 5: - return "difficulty_09_btn_001.png"; - case 6: - return "difficulty_10_btn_001.png"; - default: - return "difficulty_06_btn_001.png"; - } -} - -const char* getDifficultyIcon(int stars) { - switch(stars){ - case 10: - return "difficulty_01_btn_001.png"; - case 20: - return "difficulty_02_btn_001.png"; - case 30: - return "difficulty_03_btn_001.png"; - case 40: - return "difficulty_04_btn_001.png"; - case 50: - return "difficulty_05_btn_001.png"; - case 60: - return "difficulty_auto_btn_001.png"; - default: - return "difficulty_00_btn_001.png"; - } -} - -void EventsPush::destroySelf() { - auto scene = CCDirector::sharedDirector()->getRunningScene(); - auto layer = reinterpret_cast(scene->getChildByTag(1932)); - if (layer != nullptr) { - scene->removeChildByTag(1932); - } - EventsPush::eventCompletedCallback(scene); -} -// stole from GDR mod lol -GJDifficulty getDifficulty(int stars) { - switch(stars){ - case 0: - return GJDifficulty::NA; - case 10: - return GJDifficulty::Easy; - case 20: - return GJDifficulty::Normal; - case 30: - return GJDifficulty::Hard; - case 40: - return GJDifficulty::Harder; - case 50: - return GJDifficulty::Insane; - case 60: - return GJDifficulty::Auto; - default: - return GJDifficulty::NA; - } -} - -GJDifficulty getDemonDifficulty(int stars) { - switch(stars){ - case 3: - return GJDifficulty::DemonEasy; - case 4: - return GJDifficulty::DemonMedium; - case 5: - return GJDifficulty::DemonInsane; - case 6: - return GJDifficulty::DemonExtreme; - default: - return GJDifficulty::Demon; - } -} - -std::vector split_str(std::string &string, char separator) { - std::stringstream segmentstream(string); - std::string segmented; - std::vector splitlist; - - while (std::getline(segmentstream, segmented, separator)) { - splitlist.push_back(segmented); - } - - return splitlist; -}; - -GJGameLevel* EventsPush::convertLevelToJSON(std::string& data) { - // Robtop to JSON - GJGameLevel* level = GJGameLevel::create(); -#ifdef GEODE_IS_WINDOWS - auto gamelevelmanager = GameLevelManager::sharedState(); - auto gamestatslevel = GameStatsManager::sharedState(); - - std::string auto_level = "0"; - std::string demon = "0"; - auto datas_list = split_str(data, '#'); - std::string level_data = datas_list[0]; - std::string creator_data = datas_list[1]; - - // Level data - auto level_data_list = split_str(level_data, ':'); - if (level_data_list[21] != "") { - demon = level_data_list[21]; // - } else { - demon = "0"; - } - if (level_data_list[25] != "") { - auto_level = level_data_list[25]; // - } else { - auto_level = "0"; - } - - level->m_levelID = numFromString(level_data_list[1]).unwrapOrDefault(); - - level->m_levelName = level_data_list[3]; - level->m_levelDesc = level_data_list[35]; - level->m_levelVersion = numFromString(level_data_list[5]).unwrapOrDefault(); - level->m_userID = numFromString(level_data_list[7]).unwrapOrDefault(); - - level->m_ratings = numFromString(level_data_list[9]).unwrapOrDefault(); - level->m_ratingsSum = numFromString(level_data_list[11]).unwrapOrDefault(); - - if (level_data_list[9] == "10") { - if (demon == "1") { - std::string levelDemonDifficultyStr = level_data_list[23]; - level->m_difficulty = getDemonDifficulty(numFromString(levelDemonDifficultyStr).unwrapOrDefault()); - } else { - if (auto_level == "1") { - level->m_difficulty = getDifficulty(0); - } else { - level->m_difficulty = getDifficulty(numFromString(level_data_list[11]).unwrapOrDefault()); - } - } - } - level->m_downloads = numFromString(level_data_list[13]).unwrapOrDefault(); - if (level_data_list[53] == "0") { - level->m_audioTrack = numFromString(level_data_list[15]).unwrapOrDefault(); - } else { - level->m_songID = numFromString(level_data_list[53]).unwrapOrDefault(); - } - - std::string levelGameVersionStr = level_data_list[17]; - level->m_gameVersion = numFromString(levelGameVersionStr).unwrapOrDefault(); - int levelLikes = numFromString(level_data_list[19]).unwrapOrDefault(); - if (levelLikes >= 0) { - level->m_likes = levelLikes; - level->m_dislikes = 0; - } else { - level->m_likes = 0; - level->m_dislikes = 0 - levelLikes; - } - level->m_levelLength = numFromString(level_data_list[37]).unwrapOrDefault(); - - level->m_demon = numFromString(demon).unwrapOrDefault(); - level->m_stars = numFromString(level_data_list[27]).unwrapOrDefault(); - - if (auto_level == "1") { - level->m_autoLevel = true; - } else { - level->m_autoLevel = false; - } - - level->m_originalLevel = numFromString(level_data_list[39]).unwrapOrDefault(); - if (level_data_list[41] == "1") { - level->m_twoPlayerMode = true; - } else { - level->m_twoPlayerMode = false; - } - - level->m_coins = numFromString(level_data_list[43]).unwrapOrDefault(); - level->m_coinsVerified = numFromString(level_data_list[45]).unwrapOrDefault(); - level->m_starsRequested = numFromString(level_data_list[47]).unwrapOrDefault(); - level->m_featured = numFromString(level_data_list[29]).unwrapOrDefault(); - if (level_data_list[31] == "1") { - level->m_isEpic = true; - } else { - level->m_isEpic = false; - } - level->m_demonDifficulty = numFromString(level_data_list[23]).unwrapOrDefault(); - - level->m_objectCount = numFromString(level_data_list[33]).unwrapOrDefault(); - - level->m_levelNotDownloaded = true; - - level->m_workingTime = numFromString(level_data_list[49]).unwrapOrDefault(); - - CCArray* levelids = gamelevelmanager->m_onlineLevels->allKeys(); - CCObject* obj; - CCARRAY_FOREACH(levelids, obj) { - std::string levelid = (static_cast(obj))->getCString(); - if (numFromString(levelid).unwrapOrDefault() == level->m_levelID) { - auto levelFromSaved = static_cast(gamelevelmanager->m_onlineLevels->objectForKey(levelid)); - level->m_normalPercent = levelFromSaved->m_normalPercent; - level->m_newNormalPercent2 = levelFromSaved->m_newNormalPercent2; - level->m_practicePercent = levelFromSaved->m_practicePercent; - level->m_levelFavorited = levelFromSaved->m_levelFavorited; - level->m_levelFolder = levelFromSaved->m_levelFolder; - level->m_attempts = levelFromSaved->m_attempts; - level->m_jumps = levelFromSaved->m_jumps; - level->m_clicks = levelFromSaved->m_clicks; - level->m_attemptTime = levelFromSaved->m_attemptTime; - - level->m_levelNotDownloaded = levelFromSaved->m_levelNotDownloaded; - level->m_chk = levelFromSaved->m_chk; - level->m_isChkValid = levelFromSaved->m_isChkValid; - level->m_isCompletionLegitimate = levelFromSaved->m_isCompletionLegitimate; - level->m_isVerified = levelFromSaved->m_isVerified; - level->m_isUploaded = levelFromSaved->m_isUploaded; - } - } - - // Creator data - auto creator_data_list = split_str(creator_data, ':'); - level->m_creatorName = creator_data_list[1]; - level->m_accountID = numFromString(creator_data_list[2]).unwrapOrDefault(); -#endif - return level; -} - -void EventsPush::onClickBtn(CCObject* ret) { - auto scene = CCDirector::sharedDirector()->getRunningScene(); - auto layer = scene->getChildren()->objectAtIndex(0); - if (layer == nullptr) return; - auto events_layer = reinterpret_cast(scene->getChildByTag(1932)); - if (events_layer == nullptr) return; - std::string layerName = typeid(*layer).name() + 6; - if (layerName != "PlayLayer" && layerName != "PauseLayer" && layerName != "LevelEditorLayer") { // redirect to level - if (eventType == EventType::Rate) { - if (events_layer->level->m_levelID == 0) return; - - std::string const& url = "http://www.boomlings.com/database/getGJLevels21.php"; - #ifndef GEODE_IS_MACOS - int level_id = events_layer->level->m_levelID.value(); - #else // mac os - int level_id = events_layer->levelId; - #endif - std::string const& fields = "secret=Wmfd2893gb7&type=0&str=" + std::to_string(level_id); - - geode::utils::web::WebRequest request = web::WebRequest(); - RUNNING_REQUESTS.emplace( - "@loaderEventRateNotification", - request.bodyString(fields).post(url).map( - [](web::WebResponse* response) { - if (response->ok()) { - if (response->data().empty()) { - FLAlertLayer::create(nullptr, - "Error", - "An server error happened.", - "OK", - nullptr, - 180.0F - )->show(); - } else { - auto data_result = response->string().unwrapOrDefault(); - if (data_result != "-1") { - auto scene = CCScene::create(); - auto layer = LevelInfoLayer::create(EventsPush::convertLevelToJSON(data_result), false); - layer->downloadLevel(); - scene->addChild(layer); - CCDirector::sharedDirector()->pushScene(cocos2d::CCTransitionFade::create(0.5f, scene)); - } else { - log::info("Level not found. (-1)"); - } - } - } else { - FLAlertLayer::create(nullptr, - "Error", - "An server error happened.", - "OK", - nullptr, - 180.0F - )->show(); - } - - RUNNING_REQUESTS.erase("@loaderEventRateNotification"); - return *response; - } - ) - ); - } else if (eventType == EventType::Daily) { - DailyLevelPage::create(GJTimedLevelType::Daily)->show(); - return; - } else if (eventType == EventType::Weekly) { - DailyLevelPage::create(GJTimedLevelType::Weekly)->show(); - return; - } else if (eventType == EventType::Event) { - DailyLevelPage::create(GJTimedLevelType::Event)->show(); - return; - } else if (eventType == EventType::List) { - if (events_layer->level->m_levelID == 0) return; - - GJSearchObject* search_obj = GJSearchObject::create(SearchType::Search, std::to_string(events_layer->level->m_levelID)); - search_obj->m_searchMode = 1; - LevelBrowserLayer::create(search_obj)->show(); - return; - } /*else if (eventType == EventType::smallChest) { - RewardsPage::create()->show(); - return; - }*/ - } else { // copy to clipboard - #ifndef GEODE_IS_MACOS - clipboard::write(std::to_string(events_layer->level->m_levelID)); - #else - clipboard::write(std::to_string(events_layer->levelId)); - #endif - } -} - -bool EventsPush::init(EventData data) { - auto scene = CCDirector::sharedDirector()->getRunningScene(); - // type - // 0 = new rate - // 1 = daily - // 2 = weekly - - // rate - // 0 = unrated / star rate - // 1 = featured - // 2 = epic - // {'demon': 0, 'type': 0, 'starsum': 20, 'stars': 2, 'rate': 2, 'title': 'New Rated Level !', 'level_name': 'Pourquoi', 'level_creator': 'by Jouca', 'sprite': 'GJ_square01.png'} - int starsum = data.starsum; - int stars = data.stars; - int rateType = data.rate; - int coins = data.coins; - int areCoinsVerified = data.verified_coins; - int level_id = 0; - if (data.level_id != -1) { - level_id = data.level_id; - level->m_levelName = data.level_name; - level->m_coins = coins; - level->m_coinsVerified = areCoinsVerified; - level->m_levelID = level_id; - #ifdef GEODE_IS_MACOS - this->levelId = level_id; - #endif - } - - - auto menu = CCMenu::create(); - - bool newRate = Mod::get()->template getSettingValue("newRate"); - bool daily = Mod::get()->template getSettingValue("daily"); - bool weekly = Mod::get()->template getSettingValue("weekly"); - bool event = Mod::get()->template getSettingValue("event"); - bool smallChest = Mod::get()->template getSettingValue("smallChest"); - bool largeChest = Mod::get()->template getSettingValue("largeChest"); - bool list = Mod::get()->template getSettingValue("newListRate"); - bool announcement = Mod::get()->template getSettingValue("announcement"); - eventType = data.type; - if (eventType == EventType::Rate && !newRate) { - EventsPush::eventCompletedCallback(scene); - return true; - } - if (eventType == EventType::Daily && !daily) { - EventsPush::eventCompletedCallback(scene); - return true; - } - if (eventType == EventType::Weekly && !weekly) { - EventsPush::eventCompletedCallback(scene); - return true; - } - if (eventType == EventType::smallChest && !smallChest) { - EventsPush::eventCompletedCallback(scene); - return true; - } - if (eventType == EventType::largeChest && !largeChest) { - EventsPush::eventCompletedCallback(scene); - return true; - } - if (eventType == EventType::List && !list) { - EventsPush::eventCompletedCallback(scene); - return true; - } - if (eventType == EventType::Event && !event) { - EventsPush::eventCompletedCallback(scene); - return true; - } - if (eventType == EventType::Announcement && !announcement) { - EventsPush::eventCompletedCallback(scene); - return true; - } - auto director = CCDirector::sharedDirector(); - auto winSize = director->getWinSize(); - auto bg = cocos2d::extension::CCScale9Sprite::create(data.sprite.c_str(), { .0f, .0f, 80.0f, 80.0f, }); - auto bg_click_spr = cocos2d::extension::CCScale9Sprite::create(data.sprite.c_str(), { .0f, .0f, 80.0f, 80.0f, }); - float lrScale = (float)Mod::get()->template getSettingValue("size"); - auto LrSize = CCSize{ 240, 70 }; // * lrScale for both - bg->setScale(lrScale); - this->setZOrder(zOrder); - zOrder = zOrder + 10; - bg->setContentSize(LrSize); - bg_click_spr->setContentSize(LrSize); - // 0 / 0 = bottom left - // screen width / screen height = top right - // screen width divided by 2 / screen height divided by 2 = middle - - // TODO : click to notifications to be redirected to specific content - auto layer = scene->getChildren()->objectAtIndex(0); - std::string layerName = typeid(*layer).name() + 6; - if (layerName != "PlayLayer" && layerName != "PauseLayer" && layerName != "LevelEditorLayer") { - //auto bg_btn = CCMenuItemSpriteExtra::create(bg_click_spr, this, menu_selector(EventsPush::onClickBtn)); - menu->addChild(bg_click_spr); - } - - menu->setPosition({ bg->getContentSize().width / 2, bg->getContentSize().height / 2 }); - bg->addChild(menu); - this->addChild(bg); - - auto node = CCNode::create(); - - if (eventType != EventType::smallChest && eventType != EventType::largeChest && eventType != EventType::Announcement) { - CCSprite* diffFace; - GJDifficultySprite* mythic = nullptr; - if (!data.demon) { - diffFace = cocos2d::CCSprite::createWithSpriteFrameName(getDifficultyIcon(starsum)); - mythic = GJDifficultySprite::create(static_cast(getDifficulty(starsum)), static_cast(0)); - } else { - diffFace = cocos2d::CCSprite::createWithSpriteFrameName(getDemonDifficultyIcon(starsum)); - mythic = GJDifficultySprite::create(static_cast(getDemonDifficulty(starsum)), static_cast(0)); - } - - diffFace->setPosition({26.f, 43.f}); - diffFace->setScale(.8F); - CCSprite* moon = cocos2d::CCSprite::createWithSpriteFrameName("moon_small01_001.png"); - CCSprite* featured = cocos2d::CCSprite::createWithSpriteFrameName("GJ_featuredCoin_001.png"); - CCSprite* epic = cocos2d::CCSprite::createWithSpriteFrameName("GJ_epicCoin_001.png"); - CCSprite* legendary = cocos2d::CCSprite::createWithSpriteFrameName("GJ_epicCoin2_001.png"); - mythic->updateFeatureState(static_cast(4)); - - std::string starcountstr = std::to_string(stars); - auto starcount = cocos2d::CCLabelBMFont::create(starcountstr.c_str(), "bigFont.fnt"); - starcount->setPosition({25, 18}); - starcount->setAnchorPoint({1, 0.5}); - starcount->setScale(0.35f); - bg->addChild(starcount); - CCSprite* star; - if (eventType == EventType::List) { - star = cocos2d::CCSprite::createWithSpriteFrameName("diamond_small01_001.png"); - } else { - star = cocos2d::CCSprite::createWithSpriteFrameName("star_small01_001.png"); - } - star->setPosition({31, 18}); - star->setScale(.775F); - moon->setPosition({31, 18}); - moon->setScale(.775F); - - if (data.platformer) bg->addChild(moon); - else bg->addChild(star); - - featured->setPosition({26.f, 43.f}); - epic->setPosition({26.f, 43.f}); - legendary->setPosition({26.f, 43.f}); - mythic->setPosition({26.f, 43.f}); - featured->setScale(.8F); - epic->setScale(.8F); - legendary->setScale(.8F); - mythic->setScale(.8F); - - switch (rateType) { - default: // Rate - bg->addChild(diffFace); - break; - case 1: // Featured - bg->addChild(featured); - bg->addChild(diffFace); - break; - case 2: // Epic - bg->addChild(epic); - bg->addChild(diffFace); - break; - case 3: // Legendary - bg->addChild(legendary); - bg->addChild(diffFace); - - if (Mod::get()->template getSettingValue("customDifficultyFaces")) { - CCSprite* legendaryFace = nullptr; - if (!data.demon) { - if (starsum >= 10) { - std::string diffStr = std::to_string(static_cast(getDifficulty(starsum))); - auto name = "difficulty_0" + diffStr + "_legendaryIcon.png"; - legendaryFace = CCSprite::create((""_spr+name).c_str()); - } - } else { - if (starsum < 6) { - std::string diffStr = std::to_string(static_cast(getDemonDifficulty(starsum))); - auto name = "difficulty_0" + diffStr + "_legendaryIcon.png"; - legendaryFace = CCSprite::create((""_spr+name).c_str()); - } else { - std::string diffStr = std::to_string(static_cast(getDemonDifficulty(starsum))); - auto name = "difficulty_" + diffStr + "_legendaryIcon.png"; - legendaryFace = CCSprite::create((""_spr+name).c_str()); - } - } - - if (legendaryFace != nullptr) { - legendaryFace->setPosition({26.f, 48.f}); - legendaryFace->setScale(.8F); - bg->addChild(legendaryFace); - } - } - - break; - case 4: // Mythic - bg->addChild(mythic); - - if (Mod::get()->template getSettingValue("customDifficultyFaces")) { - CCSprite* mythicFace = nullptr; - if (!data.demon) { - if (starsum >= 10) { - std::string diffStr = std::to_string(static_cast(getDifficulty(starsum))); - auto name = "difficulty_0" + diffStr + "_mythicIcon.png"; - mythicFace = CCSprite::create((""_spr+name).c_str()); - } - } else { - if (starsum < 6) { - std::string diffStr = std::to_string(static_cast(getDemonDifficulty(starsum))); - auto name = "difficulty_0" + diffStr + "_mythicIcon.png"; - mythicFace = CCSprite::create((""_spr+name).c_str()); - } else { - std::string diffStr = std::to_string(static_cast(getDemonDifficulty(starsum))); - auto name = "difficulty_" + diffStr + "_mythicIcon.png"; - mythicFace = CCSprite::create((""_spr+name).c_str()); - } - } - - if (mythicFace != nullptr) { - mythicFace->setPosition({26.f, 48.f}); - mythicFace->setScale(.8F); - bg->addChild(mythicFace); - } - } - - break; - } - - auto verifiedCoinSpr1 = cocos2d::CCSprite::createWithSpriteFrameName("GJ_coinsIcon2_001.png"); - verifiedCoinSpr1->setScale(0.35f); - auto verifiedCoinSpr2 = cocos2d::CCSprite::createWithSpriteFrameName("GJ_coinsIcon2_001.png"); - verifiedCoinSpr2->setScale(0.35f); - auto verifiedCoinSpr3 = cocos2d::CCSprite::createWithSpriteFrameName("GJ_coinsIcon2_001.png"); - verifiedCoinSpr3->setScale(0.35f); - - if (areCoinsVerified == 0) { - verifiedCoinSpr1->setColor({255, 175, 75}); - verifiedCoinSpr2->setColor({255, 175, 75}); - verifiedCoinSpr3->setColor({255, 175, 75}); - } - - if (coins == 1 || coins == 3) { - verifiedCoinSpr2->setPosition({ -52, -22 }); - node->addChild(verifiedCoinSpr2); - - if (coins == 3) { - verifiedCoinSpr1->setPosition({ -60, -22 }); - verifiedCoinSpr3->setPosition({ -44, -22 }); - - node->addChild(verifiedCoinSpr1); - node->addChild(verifiedCoinSpr3); - } - } else if (coins == 2) { - verifiedCoinSpr1->setPosition({ -55, -22 }); - verifiedCoinSpr2->setPosition({ -48, -22 }); - - node->addChild(verifiedCoinSpr1); - node->addChild(verifiedCoinSpr2); - } - - // Progress Bar for lists - if (eventType == EventType::List) { - auto barSpriteBack = CCSprite::create("GJ_progressBar_001.png"); - barSpriteBack->setScaleX(0.38f); - barSpriteBack->setScaleY(0.45f); - barSpriteBack->setPosition({ 38, 3 }); - barSpriteBack->setPositionY(3 - 19.f); - barSpriteBack->setPositionX(38); - barSpriteBack->setColor({0, 0, 0}); - barSpriteBack->setOpacity(100); - barSpriteBack->setZOrder(10); - barSpriteBack->setID("progress-bar-list"_spr); - node->addChild(barSpriteBack); - - auto barSpriteTop = CCSprite::create("GJ_progressBar_001.png"); - barSpriteTop->setScaleY(0.4f); - barSpriteTop->setPosition({ 2, 10 }); - barSpriteTop->setAnchorPoint({0, 0.5}); - barSpriteTop->setOpacity(255); - barSpriteTop->setZOrder(11); - - CCPoint rectangle[4] = { - CCPoint(0, 0), - CCPoint(0, 20), - CCPoint(barSpriteBack->getScaledContentSize().width * -2.5f, 20), - CCPoint(barSpriteBack->getScaledContentSize().width * -2.5f, 0) - }; - - auto clippingNode = CCClippingNode::create(); - auto barMask = CCDrawNode::create(); - barMask->drawPolygon(rectangle, 4, ccc4FFromccc3B({0, 0, 0}), 0, ccc4FFromccc3B({0, 0, 0})); - clippingNode->setStencil(barMask); - clippingNode->addChild(barSpriteTop); - clippingNode->setPositionX(330); - barSpriteBack->addChild(clippingNode); - barSpriteTop->setPositionX(-666); - - // Get datas for levels - int maxToCompleteList = data.maxToCompleteList; - std::vector levels = split_str(data.levels_list, ','); - - // Get client data for levels - GameLevelManager* gameLevelManager = GameLevelManager::sharedState(); - CCArray* levelsData = gameLevelManager->getCompletedLevels(false); - - int completedLevels = 0; - int nbLevels = 0; - - while (levels.size() > 0) { - std::string id = levels[0]; - nbLevels++; - - for (int i = 0; i < levelsData->count(); i++) { - auto level = static_cast(levelsData->objectAtIndex(i)); - if (std::to_string(level->m_levelID) == id) { - if (level->m_normalPercent == 100) completedLevels++; - } - } - - levels.erase(levels.begin()); - } - - // min -666 | max -337 - // Calculate position for Progress Bar (between these values based on percentage) - float percentage; - CCLabelBMFont* progressText; - - if (completedLevels >= maxToCompleteList) { - percentage = (float)completedLevels / (float)nbLevels; - progressText = CCLabelBMFont::create((std::to_string(completedLevels) + "/" + std::to_string(nbLevels)).c_str(), "bigFont.fnt"); - barSpriteTop->setColor({ 100, 255, 255 }); - starcount->setColor({ 100, 255, 255 }); - - auto listCompleted = CCSprite::createWithSpriteFrameName("GJ_completesIcon_001.png"); - listCompleted->setPosition({ 94, -15 }); - listCompleted->setScale(0.55f); - listCompleted->setZOrder(13); - node->addChild(listCompleted); - } else { - percentage = (float)completedLevels / (float)maxToCompleteList; - progressText = CCLabelBMFont::create((std::to_string(completedLevels) + "/" + std::to_string(maxToCompleteList)).c_str(), "bigFont.fnt"); - barSpriteTop->setColor({ 255, 84, 50 }); - - auto listUncompleted = CCSprite::createWithSpriteFrameName("diamond_small01_001.png"); - listUncompleted->setPosition({ 94, -15 }); - listUncompleted->setScale(0.55f); - listUncompleted->setZOrder(13); - node->addChild(listUncompleted); - } - - auto moveAction = CCEaseSineOut::create(CCMoveBy::create(0.f, { (percentage * 337), 0 })); - barSpriteTop->runAction(moveAction); - - // Add text for Progress Bar - progressText->setPosition({ 38, -15 }); - progressText->setScale(0.35f); - progressText->setZOrder(13); - node->addChild(progressText); - } - } - - auto title = cocos2d::CCLabelBMFont::create(data.title.c_str(), "goldFont.fnt"); - if (eventType == EventType::smallChest) { - title->setPosition({ -65, 26 }); - } else if (eventType == EventType::largeChest) { - title->setPosition({ -54, 26 }); - } else if (eventType == EventType::List) { - title->setPosition({ -27, 27 }); - } else if (eventType == EventType::Announcement) { - title->setString("Announcement"); - title->setPosition({ -65, 26 }); - auto subtitle = cocos2d::CCLabelBMFont::create(data.title.c_str(), "bigFont.fnt"); - subtitle->limitLabelWidth(200, 0.46f, 0.1f); - subtitle->setAnchorPoint({ 0, 0.5 }); - subtitle->setPosition({ -65, 10 }); - node->addChild(subtitle); - } else { - title->setPosition({ -27, 23 }); - } - title->setScale(.575F); - title->setAnchorPoint({ 0, 0.5 }); - node->addChild(title); - if (eventType != EventType::Announcement) { - auto level_title = cocos2d::CCLabelBMFont::create(data.level_name.c_str(), "bigFont.fnt"); - if (eventType == EventType::List) { - level_title->setPosition({ -27, 11 }); - } else { - level_title->setPosition({ -27, 3 }); - } - level_title->setScale(.46F); - - level_title->setAnchorPoint({ 0, 0.5 }); - - auto level_by = cocos2d::CCLabelBMFont::create(data.level_creator.c_str(), "goldFont.fnt"); - if (eventType == EventType::List) { - level_by->setPosition({ -27, -2 }); - } else { - level_by->setPosition({ -27, -11 }); - } - level_by->setScale(.46F); - level_by->limitLabelWidth(120, 0.46f, 0.1f); - level_by->setAnchorPoint({ 0, 0.5 }); - node->addChild(level_by); - level_title->limitLabelWidth(120, 0.46f, 0.1f); - node->addChild(level_title); - } - - if (eventType == EventType::Daily || eventType == EventType::Weekly || eventType == EventType::Event) { - CCSprite* crown; - if (eventType == EventType::Daily) { - crown = cocos2d::CCSprite::createWithSpriteFrameName("gj_dailyCrown_001.png"); - } else if (eventType == EventType::Event) { - crown = cocos2d::CCSprite::createWithSpriteFrameName("gj_eventCrown_001.png"); - } else { - crown = cocos2d::CCSprite::createWithSpriteFrameName("gj_weeklyCrown_001.png"); - } - crown->setScale(.45F); - crown->setPosition({ 45, 45 }); - node->addChild(crown); - } else if (eventType == EventType::smallChest) { - auto chest = cocos2d::CCSprite::createWithSpriteFrameName("chest_01_02_001.png"); - chest->setScale(.45F); - chest->setPosition({ 45, -3 }); - node->addChild(chest); - } else if (eventType == EventType::largeChest) { - auto chest = cocos2d::CCSprite::createWithSpriteFrameName("chest_02_02_001.png"); - chest->setScale(.4F); - chest->setPosition({ 45, -3 }); - node->addChild(chest); - - } else if (eventType == EventType::List) { - auto list = cocos2d::CCSprite::createWithSpriteFrameName("GJ_listAddBtn_001.png"); - list->setPosition({ 139, -8 }); - node->addChild(list); - } - - node->setAnchorPoint({ 0.5, 0.5 }); - node->setPosition({ 77, 30 }); - - bg->addChild(node); - - this->setTag(1932); - - // Move action - //CCDelayTime - - float delayTime = (float)Mod::get()->template getSettingValue("time"); - - auto cornerId = Mod::get()->getSettingValue("notificationPlacement"); - float moveX = .0F; - switch (cornerId) { - case SettingPosEnum::TopLeft: // top left - bg->setPosition((-(bg->getContentSize().width / 2)) * lrScale, ((winSize.height - (bg->getContentSize().height / 2)) - (20 * lrScale))); - moveX = (bg->getContentSize().width) * lrScale; - break; - case SettingPosEnum::TopRight: // top right - bg->setPosition(winSize.width + ((bg->getContentSize().width / 2) * lrScale), ((winSize.height - (bg->getContentSize().height / 2)) - (20 * lrScale))); - moveX = -(bg->getContentSize().width * lrScale); - break; - case SettingPosEnum::BottomLeft: // bottom left - bg->setPosition((-(bg->getContentSize().width / 2)) * lrScale, (bg->getContentSize().height / 2) * lrScale); - moveX = (bg->getContentSize().width) * lrScale; - break; - case SettingPosEnum::BottomRight: // bottom right - bg->setPosition({ winSize.width + ((bg->getContentSize().width / 2) * lrScale), (bg->getContentSize().height / 2) * lrScale}); - moveX = -((bg->getContentSize().width) * lrScale); - break; - } - - bg->runAction(CCSequence::create( - CCEaseOut::create(CCMoveBy::create(0.5f, { moveX, 0.0f }), 0.6f), - CCDelayTime::create(delayTime), - CCEaseIn::create(CCMoveBy::create(0.5f, { -moveX, 0.0f }), 0.6f), - CCDelayTime::create(0.5F), - CCCallFunc::create(this, callfunc_selector(EventsPush::destroySelf)), - nullptr - )); - - float volume = 0.0f; - if (Mod::get()->template getSettingValue("sfx-link")) { - volume = GameManager::get()->m_sfxVolume; - } else { - volume = Mod::get()->template getSettingValue("sfx-volume"); - } - - if (Mod::get()->template getSettingValue("sfx") && (eventType != EventType::smallChest && eventType != EventType::largeChest)) FMODAudioEngine::sharedEngine()->playEffect("crystal01.ogg", 1, 1, volume); - if (Mod::get()->template getSettingValue("sfx") && (eventType == EventType::smallChest || eventType == EventType::largeChest)) FMODAudioEngine::sharedEngine()->playEffect("reward01.ogg", 1, 1, volume); - return true; -} - -void EventsPush::stopNow(CCScene* scene) { - EventsPush::eventCompletedCallback(scene); -} - -void EventsPush::pushRateLevel(CCScene* self, std::string data) { - EventData eventData = matjson::Serialize::fromJson(matjson::parse(data).unwrapOrDefault()).unwrapOrDefault(); - /*log::info( - "EventData [\ndemon = {};\nstarsum = {};\nstars = {};\nrate = {};\ntype = {};\ntitle = {};\nsprite = {};\nlevel_name = {};\nlevel_creator = {};\ncoins = {};\nverified_coins = {};\nplatformer = {};\nlevel_id = {};\nlevels_list = {};\nmaxToCompleteList = {};\n]", - eventData.demon, - eventData.starsum, - eventData.stars, - eventData.rate, - GDUtils::Events::RateEvent::typeToInt(eventData.type), - eventData.title, - eventData.sprite, - eventData.level_name, - eventData.level_creator, - eventData.coins, - eventData.verified_coins, - eventData.platformer, - eventData.level_id, - eventData.levels_list, - eventData.maxToCompleteList - );*/ - pushRateLevel(self, eventData); -} - -void EventsPush::pushRateLevel(CCScene* self, EventData data) { - // Enqueue the event - eventQueue.push(data); - - // If no event is being processed, start processing events - if (!processingEvents) { - EventsPush::processNextEvent(self); - } -} - -void EventsPush::processNextEvent(CCScene* self) { - if (!eventQueue.empty()) { - auto data = eventQueue.front(); - eventQueue.pop(); - processingEvents = true; - - auto layer = EventsPush::create(data); - GDUtils::Events::OnRate(data).post(); - // Set a callback function that will be called when the event is completed - //layer->setEventCompletedCallback(std::bind(&EventsPush::eventCompletedCallback, this)); - if (layer && self) self->addChild(layer); - } -} - -void EventsPush::eventCompletedCallback(CCScene* self) { - // Event is completed, set processingEvents to false - processingEvents = false; - - // Process the next event (if any) - EventsPush::processNextEvent(self); -} - -$execute { - new EventListener>(+[](GDUtils::Events::RateEvent* e) { - if (auto scene = CCScene::get()) { - EventsPush::pushRateLevel(scene, e->getData()); - } - return ListenerResult::Stop; - }); -} diff --git a/src/Notifications/EventsPush.h b/src/Notifications/EventsPush.h deleted file mode 100644 index 4ac8aec..0000000 --- a/src/Notifications/EventsPush.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once -#include -#include -#include "../../include/RateEvent.hpp" -#include "../includes.h" -using namespace geode::prelude; - -class EventsPush : public CCLayer { - protected: - GJGameLevel* level = GJGameLevel::create(); - #ifdef GEODE_IS_MACOS - int levelId = 0; // because for some reason GJGameLevel doesnt even work :( - #endif - EventType eventType = EventType::NA; - virtual bool init(EventData data); - public: - void destroySelf(); - void onClickBtn(CCObject*); - static EventsPush* create(EventData data); - static void pushRateLevel(CCScene* self, std::string data); - static void pushRateLevel(CCScene* self, EventData data); - static void processNextEvent(CCScene* self); - static void eventCompletedCallback(CCScene* self); - static void stopNow(CCScene* scene); - static GJGameLevel* convertLevelToJSON(std::string& data); - static std::vector getWords(std::string s, std::string delim); -}; diff --git a/src/OldBorder.cpp b/src/OldBorder.cpp deleted file mode 100644 index 937e2b6..0000000 --- a/src/OldBorder.cpp +++ /dev/null @@ -1,170 +0,0 @@ -#include "OldBorder.hpp" - -OldBorder* OldBorder::create(CCNode* node, const ccColor4B& backgroundColor, const CCSize& size, const CCPoint& padding) { - OldBorder* instance = new OldBorder(padding); - - if (instance && instance->init(node, backgroundColor, size)) { - instance->autorelease(); - - return instance; - } else { - CC_SAFE_DELETE(instance); - - return nullptr; - } -} - -OldBorder::OldBorder(const CCPoint& padding) : m_padding({ padding.x, padding.y, padding.x, padding.y }) { } - -bool OldBorder::init(const ccColor4B& backgroundColor, const CCSize& size) { - return this->init(nullptr, backgroundColor, size); -} - -bool OldBorder::init(CCNode* node, const ccColor4B& backgroundColor, const CCSize& size) { - if (!this->initWithColor(backgroundColor)) { - return false; - } - - CCScale9Sprite* OldBorder = CCScale9Sprite::create("inverseborder.png"_spr); - CCLayer* content = CCLayer::create(); - - OldBorder->setID("border_sprite"_spr); - OldBorder->setAnchorPoint({ 0, 0 }); - OldBorder->setPosition({ 0, 0 }); - OldBorder->setZOrder(1); - content->setID("border_content"_spr); - content->setContentSize(size); - this->addChild(OldBorder); - this->addChild(content); - - if (node != nullptr) { - this->setNode(node); - } - - this->setSize(size); - - return true; -} - -void OldBorder::setPadding(const CCPoint& padding) { - this->setPadding(padding.x, padding.y); -} - -void OldBorder::setPadding(float x, float y) { - this->setPaddingX(x); - this->setPaddingY(y); -} - -void OldBorder::setPadding(float padding) { - this->setPadding(padding, padding); -} - -void OldBorder::setPaddingX(float x) { - this->setPaddingLeft(x); - this->setPaddingRight(x); -} - -OldBorder::Padding OldBorder::getPadding() { - return m_padding; -} - -float OldBorder::getPaddingX() { - return (m_padding.left + m_padding.right) / 2; -} - -void OldBorder::setPaddingY(float y) { - this->setPaddingTop(y); - this->setPaddingBottom(y); -} - -float OldBorder::getPaddingY() { - return (m_padding.top + m_padding.bottom) / 2; -} - -void OldBorder::setPaddingTop(float top) { - m_padding.top = top; - - this->updatePadding(); -} - -float OldBorder::getPaddingTop() { - return m_padding.top; -} - -void OldBorder::setPaddingRight(float right) { - m_padding.right = right; - - this->updatePadding(); -} - -float OldBorder::getPaddingRight() { - return m_padding.right; -} - -void OldBorder::setPaddingBottom(float bottom) { - m_padding.bottom = bottom; - - this->updatePadding(); -} - -float OldBorder::getPaddingBottom() { - return m_padding.bottom; -} - -void OldBorder::setPaddingLeft(float left) { - m_padding.left = left; - - this->updatePadding(); -} - -float OldBorder::getPaddingLeft() { - return m_padding.left; -} - -void OldBorder::setBackgroundColor(const ccColor4B& color) { - this->setColor({ color.r, color.g, color.b }); - this->setOpacity(color.a); -} - -ccColor4B OldBorder::getBackgroundColor() { - return { _displayedColor.r, _displayedColor.g, _displayedColor.b, _displayedOpacity }; -} - -void OldBorder::setNode(CCNode* node) { - CCNode* content = this->getChildByID("border_content"_spr); - - // Can't assume an ID as the node is a user input and may have its ID changed - if (CCNode* oldNode = cocos::getChild(content, 0)) { - // Not going to mess with releasing the node, I'll leave that to the user - oldNode->removeFromParent(); - } - - content->addChild(node); - - this->updatePadding(); -} - -CCNode* OldBorder::getNode() { - if (CCNode* node = cocos::getChild(this->getChildByID("border_content"_spr), 0)) { - return node; - } else { - return nullptr; - } -} - -void OldBorder::setSize(const CCSize& size) { - this->setContentSize(size); - this->getChildByID("border_sprite"_spr)->setContentSize(size); - this->getChildByID("border_content"_spr)->setContentSize(size); - this->updatePadding(); -} - -void OldBorder::updatePadding() { - if (CCNode* node = this->getNode()) { - CCSize size = this->getContentSize(); - - node->setAnchorPoint({ 0, 0 }); - node->setPosition({ m_padding.left, m_padding.bottom }); - node->setContentSize(size - ccp(this->getPaddingX(), this->getPaddingY()) * 2); - } -} \ No newline at end of file diff --git a/src/OldBorder.hpp b/src/OldBorder.hpp deleted file mode 100644 index 05b43b9..0000000 --- a/src/OldBorder.hpp +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once -#include "includes.h" - -class OldBorder : public cocos2d::CCLayerColor { -public: - struct Padding { - float top; - float right; - float bottom; - float left; - }; - - static OldBorder* create(cocos2d::CCNode* node, const cocos2d::ccColor4B& backgroundColor, const cocos2d::CCSize& size = { 0, 0 }, const cocos2d::CCPoint& padding = { 0, 0 }); - - void setPadding(const cocos2d::CCPoint& padding); - void setPadding(float x, float y); - void setPadding(float padding); - Padding getPadding(); - void setPaddingX(float x); - float getPaddingX(); - void setPaddingY(float y); - float getPaddingY(); - void setPaddingTop(float top); - float getPaddingTop(); - void setPaddingRight(float right); - float getPaddingRight(); - void setPaddingBottom(float bottom); - float getPaddingBottom(); - void setPaddingLeft(float left); - float getPaddingLeft(); - void setBackgroundColor(const cocos2d::ccColor4B& color); - cocos2d::ccColor4B getBackgroundColor(); - void setNode(CCNode* node); - CCNode* getNode(); - void setSize(const cocos2d::CCSize& size); -protected: - Padding m_padding; - - OldBorder(const cocos2d::CCPoint& padding = { 0, 0 }); - bool init(const cocos2d::ccColor4B& backgroundColor, const cocos2d::CCSize& size); - bool init(CCNode* node, const cocos2d::ccColor4B& backgroundColor, const cocos2d::CCSize& size); -private: - void updatePadding(); -}; \ No newline at end of file diff --git a/src/ProcessLambdas.cpp b/src/ProcessLambdas.cpp deleted file mode 100644 index ae91778..0000000 --- a/src/ProcessLambdas.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include "ProcessLambdas.h" -cocos2d::CCLayer* queueLayer = nullptr; -cocos2d::SEL_MenuHandler queueCallback = nullptr; -bool startCallbackQueue = false; -void ProcessLambdas::callMenuHandler(cocos2d::CCLayer* self, cocos2d::SEL_MenuHandler callback) { - startCallbackQueue = true; - queueLayer = self; - queueCallback = callback; -} -void ProcessLambdas::processMenuHandler() { - startCallbackQueue = false; - (queueLayer->*queueCallback)(queueLayer); - queueLayer = nullptr; - queueCallback = nullptr; -} -bool ProcessLambdas::shouldProcessMenuHandler() { - return startCallbackQueue; -} -MenuHandlerResponse ProcessLambdas::getMenuHandler() { - MenuHandlerResponse response; - response.m_queueLayer = queueLayer; - response.m_queueCallback = queueCallback; - return response; -} diff --git a/src/ProcessLambdas.h b/src/ProcessLambdas.h deleted file mode 100644 index 0277689..0000000 --- a/src/ProcessLambdas.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once -#include -// too lazy to deal with pairs or tuples -struct MenuHandlerResponse { - cocos2d::CCLayer* m_queueLayer; - cocos2d::SEL_MenuHandler m_queueCallback; -}; - -// bad coding ik, dont blame me -class ProcessLambdas { - public: - static void callMenuHandler(cocos2d::CCLayer* self, cocos2d::SEL_MenuHandler callback); - static bool shouldProcessMenuHandler(); - static MenuHandlerResponse getMenuHandler(); - static void processMenuHandler(); -}; diff --git a/src/Settings/CustomSettings.cpp b/src/Settings/CustomSettings.cpp deleted file mode 100644 index fcd38f9..0000000 --- a/src/Settings/CustomSettings.cpp +++ /dev/null @@ -1,10 +0,0 @@ -#include "CustomSettings.hpp" -#include -#include "../includes.h" - -SettingNodeV3* SettingPosValue::createNode(float width) { - return SettingPosNode::create(static_pointer_cast(shared_from_this()), width); -} -SettingNodeV3* SettingCreditsValue::createNode(float width) { - return SettingCreditsNode::create(static_pointer_cast(shared_from_this()), width); -} diff --git a/src/Settings/CustomSettings.hpp b/src/Settings/CustomSettings.hpp deleted file mode 100644 index 9bb73d9..0000000 --- a/src/Settings/CustomSettings.hpp +++ /dev/null @@ -1,272 +0,0 @@ -#pragma once -#include "CreditsBetaMenu.h" -#include "CreditsMenu.h" -#include -using namespace geode::prelude; - - - - -/* - Notification Position -*/ - -const int DEFAULT_POS = 4; - -enum class SettingPosEnum : int { - TopLeft, - TopRight, - BottomLeft, - BottomRight -}; - -template <> -struct matjson::Serialize { - static matjson::Value toJson(SettingPosEnum const& value) { - switch (value) { - case SettingPosEnum::TopLeft: - return 1; - case SettingPosEnum::TopRight: - return 2; - case SettingPosEnum::BottomLeft: - return 3; - default: - case SettingPosEnum::BottomRight: - return 4; - } - } - static Result fromJson(matjson::Value const& value) { - switch (value.asInt().unwrapOrDefault()) { - case 1: return Ok(SettingPosEnum::TopLeft); - case 2: return Ok(SettingPosEnum::TopRight); - case 3: return Ok(SettingPosEnum::BottomLeft); - case 4: return Ok(SettingPosEnum::BottomRight); - default: return Err("Invalid SettingPosEnum value '{}'", value.asInt().unwrapOrDefault()); - } - } - static bool is_json(matjson::Value const& json) { - return json.isNumber() || json.isString(); - } -}; - -class SettingPosValue : public SettingBaseValueV3 { -public: - static Result> parse(std::string const& key, std::string const& modID, matjson::Value const& json) { - auto res = std::make_shared(); - auto root = checkJson(json, "SettingPosValue"); - res->parseBaseProperties(key, modID, root); - root.checkUnknownKeys(); - return root.ok(std::static_pointer_cast(res)); - } - SettingNodeV3* createNode(float width) override; -}; - - -template <> -struct geode::SettingTypeForValueType { - using SettingType = SettingPosValue; -}; - -class SettingPosNode : public SettingValueNodeV3 { -protected: - CCMenuItemToggler* tlBtn; - CCMenuItemToggler* trBtn; - CCMenuItemToggler* blBtn; - CCMenuItemToggler* brBtn; - - int getActiveCornerTag(SettingPosEnum corner) { - switch (corner) { - case SettingPosEnum::TopLeft: // Top Left - return 2004; - case SettingPosEnum::TopRight: // Top Right - return 2005; - case SettingPosEnum::BottomLeft: // Bottom Left - return 2006; - case SettingPosEnum::BottomRight: // Bottom Right - default: - return 2007; - } - } - SettingPosEnum tagToCorner(int tag) { - switch (tag) { - case 2004: // Top Left - return SettingPosEnum::TopLeft; - case 2005: // Top Right - return SettingPosEnum::TopRight; - case 2006: // Bottom Left - return SettingPosEnum::BottomLeft; - default: - case 2007: // Bottom Right - return SettingPosEnum::BottomRight; - } - } - - bool init(std::shared_ptr setting, float width) { - if (!SettingValueNodeV3::init(setting, width)) - return false; - this->setContentSize({ width, 70.f }); - CCSprite* toggleOn = CCSprite::createWithSpriteFrameName("GJ_checkOn_001.png"); - CCSprite* toggleOff = CCSprite::createWithSpriteFrameName("GJ_checkOff_001.png"); - toggleOn->setScale(.7F); - toggleOff->setScale(.7F); - tlBtn = CCMenuItemToggler::create( - toggleOn, - toggleOff, - this, - menu_selector(SettingPosNode::onCornerClick) - ); - trBtn = CCMenuItemToggler::create( - toggleOn, - toggleOff, - this, - menu_selector(SettingPosNode::onCornerClick) - ); - blBtn = CCMenuItemToggler::create( - toggleOn, - toggleOff, - this, - menu_selector(SettingPosNode::onCornerClick) - ); - brBtn = CCMenuItemToggler::create( - toggleOn, - toggleOff, - this, - menu_selector(SettingPosNode::onCornerClick) - ); - tlBtn->setTag(getActiveCornerTag(SettingPosEnum::TopLeft)); - trBtn->setTag(getActiveCornerTag(SettingPosEnum::TopRight)); - blBtn->setTag(getActiveCornerTag(SettingPosEnum::BottomLeft)); - brBtn->setTag(getActiveCornerTag(SettingPosEnum::BottomRight)); - getButtonMenu()->setContentSize({width / 2, 45.F}); - getButtonMenu()->addChildAtPosition(tlBtn, Anchor::TopLeft); - getButtonMenu()->addChildAtPosition(trBtn, Anchor::TopRight); - getButtonMenu()->addChildAtPosition(blBtn, Anchor::BottomLeft); - getButtonMenu()->addChildAtPosition(brBtn, Anchor::BottomRight); - getButtonMenu()->updateLayout(); - getButtonMenu()->setAnchorPoint({0.5, 0.5}); - getButtonMenu()->updateAnchoredPosition(Anchor::Center, {0, 0}); - this->getNameLabel()->setVisible(false); - auto currentCorner = static_cast(this->getValue()); - tlBtn->toggle(!(tlBtn->getTag() == getActiveCornerTag(currentCorner))); - trBtn->toggle(!(trBtn->getTag() == getActiveCornerTag(currentCorner))); - blBtn->toggle(!(blBtn->getTag() == getActiveCornerTag(currentCorner))); - brBtn->toggle(!(brBtn->getTag() == getActiveCornerTag(currentCorner))); - this->updateState(nullptr); - return true; - } - - void updateState(CCNode* invoker) override { - SettingValueNodeV3::updateState(invoker); - auto currentCorner = static_cast(this->getValue()); - } - void onCornerClick(CCObject* sender) { - tlBtn->toggle(true); - trBtn->toggle(true); - blBtn->toggle(true); - brBtn->toggle(true); - SettingPosEnum value = tagToCorner(sender->getTag()); - this->setValue(value, static_cast(sender)); - }; - void onResetToDefault() override { - tlBtn->toggle(true); - trBtn->toggle(true); - blBtn->toggle(true); - brBtn->toggle(false); - this->setValue(SettingPosEnum::BottomRight, nullptr); - } -public: - static SettingPosNode* create(std::shared_ptr setting, float width) { - auto ret = new SettingPosNode(); - if (ret && ret->init(setting, width)) { - ret->autorelease(); - return ret; - } - CC_SAFE_DELETE(ret); - return nullptr; - } -}; - -/* - Credits -*/ - -class SettingCreditsValue : public SettingV3 { -public: - static Result> parse(std::string const& key, std::string const& modID, matjson::Value const& json) { - auto res = std::make_shared(); - auto root = checkJson(json, "SettingCreditsValue"); - res->init(key, modID, root); - res->parseNameAndDescription(root); - res->parseEnableIf(root); - root.checkUnknownKeys(); - return root.ok(std::static_pointer_cast(res)); - } - - bool load(matjson::Value const& json) override { - return true; - } - bool save(matjson::Value& json) const override { - return true; - } - bool isDefaultValue() const override { - return true; - } - void reset() override {} - SettingNodeV3* createNode(float width) override; -}; - - -class SettingCreditsNode : public SettingNodeV3 { -protected: - bool init(std::shared_ptr setting, float width) { - if (!SettingNodeV3::init(setting, width)) - return false; - this->setContentSize({ width, 40.f }); - auto creditsSpr = ButtonSprite::create("Credits", "bigFont.fnt", "GJ_button_01.png"); - creditsSpr->setScale(.6F); - auto creditsBtn = CCMenuItemSpriteExtra::create( - creditsSpr, - this, - menu_selector(SettingCreditsNode::onCreditsBtn) - ); - auto betaTestersSpr = ButtonSprite::create("Beta Testers", "bigFont.fnt", "GJ_button_01.png"); - betaTestersSpr->setScale(.6F); - auto betaTestersBtn = CCMenuItemSpriteExtra::create( - betaTestersSpr, - this, - menu_selector(SettingCreditsNode::onBetaBtn) - ); - getButtonMenu()->setContentWidth(width); - getButtonMenu()->addChildAtPosition(creditsBtn, Anchor::Center, {-55, 0}); - getButtonMenu()->addChildAtPosition(betaTestersBtn, Anchor::Center, {85, 0}); - getButtonMenu()->updateLayout(); - getNameLabel()->setVisible(false); - return true; - } - void onCommit() override {} - void onResetToDefault() override {} - -public: - void onCreditsBtn(CCObject*) { - CreditsMenu::create()->show(); - } - void onBetaBtn(CCObject*) { - CreditsBetaMenu::create()->show(); - } - - bool hasUncommittedChanges() const override { - return false; - } - bool hasNonDefaultValue() const override { - return false; - } - static SettingCreditsNode* create(std::shared_ptr setting, float width) { - auto ret = new SettingCreditsNode(); - if (ret && ret->init(setting, width)) { - ret->autorelease(); - return ret; - } - CC_SAFE_DELETE(ret); - return nullptr; - } -}; diff --git a/src/Settings/TestButton.cpp b/src/Settings/TestButton.cpp deleted file mode 100644 index 571104a..0000000 --- a/src/Settings/TestButton.cpp +++ /dev/null @@ -1,175 +0,0 @@ -#include -#include "../Notifications/EventsPush.h" - -using namespace geode::prelude; - -/* - --- Test --- -*/ - -int cycleTypes = -1; - -class SettingTestValue : public SettingV3 { -public: - static Result> parse(std::string const& key, std::string const& modID, matjson::Value const& json) { - auto res = std::make_shared(); - auto root = checkJson(json, "SettingTestValue"); - res->init(key, modID, root); - res->parseNameAndDescription(root); - res->parseEnableIf(root); - root.checkUnknownKeys(); - return root.ok(std::static_pointer_cast(res)); - } - - bool load(matjson::Value const& json) override { - return true; - } - bool save(matjson::Value& json) const override { - return true; - } - bool isDefaultValue() const override { - return true; - } - void reset() override {} - SettingNodeV3* createNode(float width) override; -}; - -class SettingTestNode : public SettingNodeV3 { -protected: - bool init(std::shared_ptr setting, float width) { - if (!SettingNodeV3::init(setting, width)) - return false; - this->setContentSize({ width, 35.f }); - auto label = CCLabelBMFont::create("Test", "bigFont.fnt"); - - label->setScale(.6F); - - auto playSpr = CCSprite::createWithSpriteFrameName("GJ_playBtn2_001.png"); - - playSpr->setScale(.4F); - - auto testBtn = CCMenuItemSpriteExtra::create( - playSpr, - this, - menu_selector(SettingTestNode::onTestBtn) - ); - - getButtonMenu()->setContentWidth(width); - getButtonMenu()->addChildAtPosition(label, Anchor::Left, {42, 0}); - getButtonMenu()->addChildAtPosition(testBtn, Anchor::Right, {-15, 0}); - getButtonMenu()->updateLayout(); - - getNameLabel()->setVisible(false); - - return true; - } - void onCommit() override {} - void onResetToDefault() override {} - -public: - void onTestBtn(CCObject*) { - auto scene = CCDirector::sharedDirector()->getRunningScene(); - cycleTypes = cycleTypes + 1; - if (cycleTypes > 4) cycleTypes = 0; - bool newRate = Mod::get()->getSettingValue("newRate"); - bool daily = Mod::get()->getSettingValue("daily"); - bool weekly = Mod::get()->getSettingValue("weekly"); - if (!newRate && cycleTypes == 0) cycleTypes = 1; - if (!daily && cycleTypes == 1) cycleTypes = 2; - if (!weekly && cycleTypes == 2) cycleTypes = 3; - if (!newRate && !daily && !weekly) { - cycleTypes = 5; - } - - EventData data; - data.type = EventType::Rate; - data.rate = 1; - data.coins = 3; - data.verified_coins = true; - data.level_creator = "by RobTop"; - //{'demon': 1, 'type': 1, 'starsum': 6, 'stars': 10, 'rate': 1, 'title': 'BRAINPOWER Let the base kick!', 'level_name': 'O-oooooooooo AAAAE-A-A-I-A-U-', 'level_creator': ' JO-oooooooooooo AAE-O-A-A-U-U-A- E-eee-ee-eee AAAAE-A-E-I-E-A- JO-ooo-oo-oo-oo EEEEO-A-AAA-AAAA', 'sprite': 'GJ_square01.png'} - switch (cycleTypes) { - case 0: // new rate - data.title = "New Rated Level !"; - data.stars = 1; - data.starsum = 10; - data.level_name = "Stereo Madness"; - break; - case 1: // daily - data.title = "New Daily Level !"; - data.type = EventType::Daily; - data.stars = 9; - data.starsum = 40; - data.level_name = "Cycles"; - break; - case 2: // weekly - data.title = "New Weekly Level !"; - data.demon = true; - data.type = EventType::Weekly; - data.stars = 14; - data.starsum = 7; - data.level_name = "Theory of Everything 2"; - data.sprite = "GJ_square05.png"; - break; - case 3: // Legendary rating test - data.title = "New Rated Level !"; - data.stars = 3; - data.starsum = 20; - data.level_name = "Oh no it's happening"; - data.level_creator = "by ..."; - data.rate = 3; - break; - case 4: // Mythic rating test - data.title = "New Rated Level !"; - data.stars = 100; - data.starsum = 20; - data.type = EventType::Event; - data.level_name = "FIRE IN THE HOLE!"; - data.level_creator = "by GD Community"; - data.rate = 4; - break; - case 5: // in case the user disables everything - data.title = "New Rated Level !"; - data.demon = true; - data.stars = 2; - data.starsum = 6; - data.level_name = "Back On Track"; - data.rate = 1; - break; - default: // not possible - data.title = "hi there hxd user, ghidra user or ida user. (or hi RobTop) Here's a little easter egg for you :)"; - data.demon = true; - data.stars = 14; - data.starsum = 7; - data.sprite = "This will crash! :D"; - break; - } - EventsPush::pushRateLevel(scene, data); - } - bool hasUncommittedChanges() const override { - return false; - } - bool hasNonDefaultValue() const override { - return false; - } - static SettingTestNode* create(std::shared_ptr setting, float width) { - auto ret = new SettingTestNode(); - if (ret && ret->init(setting, width)) { - ret->autorelease(); - return ret; - } - CC_SAFE_DELETE(ret); - return nullptr; - } - std::shared_ptr getSetting() const { - return std::static_pointer_cast(SettingNodeV3::getSetting()); - } -}; - -SettingNodeV3* SettingTestValue::createNode(float width) { - return SettingTestNode::create(static_pointer_cast(shared_from_this()), width); -} - -$execute { - (void)Mod::get()->registerCustomSettingType("test-notification", &SettingTestValue::parse); -} diff --git a/src/Styles/EventLevelInformation.cpp b/src/Styles/EventLevelInformation.cpp deleted file mode 100644 index 630a440..0000000 --- a/src/Styles/EventLevelInformation.cpp +++ /dev/null @@ -1,116 +0,0 @@ -#include "EventLevelInformation.h" -#include "Geode/Enums.hpp" -#include - -class $modify(EventPage, DailyLevelPage) { - void onInfo(CCObject* pSender) { - EventLevelInformation::scene(); - } - - bool init(GJTimedLevelType type) { - if (!DailyLevelPage::init(type)) return false; - - if (type != GJTimedLevelType::Event) return true; - - if (!Mod::get()->template getSettingValue("eventLevelInfo")) return true; - - auto infoSpr = CCSprite::createWithSpriteFrameName("info_btn.png"_spr); - auto infoBtn = CCMenuItemSpriteExtra::create(infoSpr, this, menu_selector(EventPage::onInfo)); - - // Put on the corner top left of the background - auto menu = as(this->m_mainLayer->getChildByIDRecursive("main-menu")); - if (menu) { - infoBtn->setPosition({ 14, -194 }); - menu->addChild(infoBtn); - } - - return true; - } -}; - -void EventLevelInformation::scene() { - auto popup = new EventLevelInformation(); - - if (popup && popup->initAnchored(400.0f, 225.0f, "GJ_square02.png")) { - popup->autorelease(); - CCDirector::sharedDirector()->getRunningScene()->addChild(popup, 1000); - } else { - CC_SAFE_DELETE(popup); - } -}; - -bool EventLevelInformation::setup() { - auto winSize = cocos2d::CCDirector::sharedDirector()->getWinSize(); - auto director = cocos2d::CCDirector::sharedDirector(); - - registerWithTouchDispatcher(); - setTouchEnabled(true); - setKeypadEnabled(true); - - EventLevelInformation::loadPage(); - return true; -} - -void EventLevelInformation::loadPage() { - // Make loading circle - loading_circle = LoadingCircle::create(); - loading_circle->setZOrder(25); - loading_circle->setParentLayer(this); - loading_circle->show(); - - // Load the image - std::string URL = "https://clarifygdps.com/gdutils/event_info/image_banner.png?randome=" + std::to_string(static_cast(time(nullptr))); - - auto req = web::WebRequest(); - m_downloadListener.bind([this](web::WebTask::Event* e){ - if (auto res = e->getValue()){ - if (!res->ok()) { - onDownloadFail(); - } else { - auto data = res->data(); - std::thread imageThread = std::thread([data,this](){ - m_image = new CCImage(); - m_image->initWithImageData(const_cast(data.data()),data.size()); - geode::Loader::get()->queueInMainThread([this](){ - imageCreationFinished(m_image); - m_image->release(); - }); - }); - imageThread.detach(); - } - } - }); - - auto downloadTask = req.header("Cache-Control", "no-cache").header("Pragma", "no-cache").get(URL); - m_downloadListener.setFilter(downloadTask); -} - -void EventLevelInformation::imageCreationFinished(CCImage* image) { - fadeLoadingCircle(); - - auto size = this->m_mainLayer->getContentSize(); - auto texture = new CCTexture2D(); - texture->initWithImage(image); - auto spr = CCSprite::createWithTexture(texture); - texture->autorelease(); - spr->setPosition({ size.width / 2, size.height / 2 }); - spr->setScale(1.1f); - - this->m_mainLayer->addChild(spr); -} - -void EventLevelInformation::onDownloadFail() { - fadeLoadingCircle(); - - auto size = this->m_mainLayer->getContentSize(); - auto failLabel = CCLabelBMFont::create("No event banner found.", "bigFont.fnt", 0.0f, kCCTextAlignmentCenter); - failLabel->setPosition({ size.width / 2, size.height / 2 }); - failLabel->setScale(0.7f); - - this->m_mainLayer->addChild(failLabel); -} - -void EventLevelInformation::fadeLoadingCircle() { - if (loading_circle == nullptr) return; - loading_circle->fadeAndRemove(); -}; diff --git a/src/Styles/EventLevelInformation.h b/src/Styles/EventLevelInformation.h deleted file mode 100644 index 9fb406d..0000000 --- a/src/Styles/EventLevelInformation.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once -#include "../includes.h" -#include "Geode/binding/FLAlertLayer.hpp" -#include "../OldBorder.hpp" - -#include - -class EventLevelInformation : public Popup<> { - protected: - EventListener m_downloadListener; - Ref m_image; - LoadingCircle* loading_circle = nullptr; - - bool setup() override; - void loadPage(); - void imageCreationFinished(CCImage* image); - void onDownloadFail(); - void fadeLoadingCircle(); - - public: - static void scene(); -}; \ No newline at end of file diff --git a/src/Styles/GauntletStyle.cpp b/src/Styles/GauntletStyle.cpp deleted file mode 100644 index 76ce68e..0000000 --- a/src/Styles/GauntletStyle.cpp +++ /dev/null @@ -1,1124 +0,0 @@ -#include "../includes.h" -#include -#include -#include "../Utils/ParticleManager.hpp" -#include "Geode/cocos/particle_nodes/CCParticleSystemQuad.h" - -class $modify(CustomGauntletSelectLayer, GauntletSelectLayer) { - void reverseGauntlet(GauntletNode* node) { - CCNode* gauntlet_node = node->m_gauntletInfoNode; - - CCNode* checkmark = node->getChildByID("checkmark-sprite"); - if (checkmark) { - checkmark->setPositionY(78); - - CCNode* checkmark_shadow = node->getChildByID("checkmark-shadow-sprite"); - checkmark_shadow->setPositionY(78); - } - - CCNode* label_reward = gauntlet_node->getChildByID("reward-label"); - if (label_reward) { - label_reward->setPositionY(50); - CCNode* label_reward_shadow = gauntlet_node->getChildByID("reward-shadow-label"); - label_reward_shadow->setPositionY(50); - - CCSprite* chest = static_cast(gauntlet_node->getChildByID("chest-sprite")); - chest->setPositionY(80); - chest->setFlipY(true); - CCSprite* chest_shadow = static_cast(gauntlet_node->getChildByID("chest-shadow-sprite")); - chest_shadow->setPositionY(80); - chest_shadow->setFlipY(true); - } - - CCNode* label_progress = gauntlet_node->getChildByID("gauntlet-progress-label"); - label_progress->setPositionY(29); - - CCNode* label_progress_shadow = gauntlet_node->getChildByID("gauntlet-progress-shadow-label"); - label_progress_shadow->setPositionY(29); - } - - bool init(int p0) { - if (!GauntletSelectLayer::init(p0)) return false; - - if (!Mod::get()->template getSettingValue("gauntletDesign")) return true; - - CCSprite* infoSpr = CCSprite::createWithSpriteFrameName("GJ_infoIcon_001.png"); - CCMenuItemSpriteExtra* infoBtn = CCMenuItemSpriteExtra::create( - infoSpr, - this, - menu_selector(CustomGauntletSelectLayer::onInfoBtn) - ); - - CCSprite* bottom_corner_right = static_cast(this->getChildByIDRecursive("bottom-right-corner")); - if (bottom_corner_right) { - infoBtn->setPosition(bottom_corner_right->getPosition()); - infoBtn->setScale(.8f); - infoBtn->setZOrder(10); - infoBtn->setColor(cocos2d::ccc3(236, 149, 19)); - infoBtn->setID("particle-info-button"_spr); - - CCMenu* menu_btr = static_cast(this->getChildByIDRecursive("bottom-right-menu")); - menu_btr->addChild(infoBtn); - menu_btr->updateLayout(); - - CCParticleSystemQuad* particle_info = GameToolbox::particleFromString("26a-1a1.66a0.3a30a90a180a14a0a1a1a0a7a0a0a0a0a2a1a0a0a1a0a0.568627a0a0.12549a0a1a0a1a1a0a0a1a0a0.501961a0a0a0a1a0a0a0a0.46a0a0a0a0a0a0a0a0a2a1a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - infoBtn->addChild(particle_info); - particle_info->setPosition(infoSpr->getPosition()); - particle_info->setZOrder(9); - } - - return true; - } - - void onInfoBtn(CCObject* pSender) { - FLAlertLayer::create( - nullptr, - "Gauntlets redesign", - R"text( - All new designs for Gauntlets are made by xvia and brift. - Particles are made by GammaReXx. - - Thanks for their contributions! - )text", - "OK", - nullptr, - 300.0f - )->show(); - } - - void setupGauntlets() { - GauntletSelectLayer::setupGauntlets(); - - if (!Mod::get()->template getSettingValue("gauntletDesign")) return; - if (!m_scrollLayer) return; - - for (int i = 0; i < m_scrollLayer->m_pages->count(); i++) { - auto page = static_cast(static_cast(m_scrollLayer->m_pages->objectAtIndex(i))->getChildren()->objectAtIndex(0))->getChildren(); - - for (int j = 0; j < page->count(); j++) { - CCNode* item = static_cast(page->objectAtIndex(j)); - - // Handle spaces - if (j == 0 && page->count() > 1) { - item->setPositionX(item->getPositionX() - 25.0f); - } else if (j == 1 && page->count() == 2) { - item->setPositionX(item->getPositionX() + 25.0f); - } else if (j == 2) { - item->setPositionX(item->getPositionX() + 25.0f); - } - - GauntletNode* node = static_cast(item->getChildByIDRecursive("gauntlet-node")); - - CCLabelBMFont* label = static_cast(node->getChildByID("gauntlet-name-label")); - CCNode* label_shadow = node->getChildByID("gauntlet-name-shadow-label"); - CCNode* label_gauntlet = node->getChildByID("gauntlet-label"); - CCNode* label_shadow_gauntlet = node->getChildByID("gauntlet-shadow-label"); - CCNode* bg = node->getChildByID("background"); - - CCNode* gauntlet_node = node->m_gauntletInfoNode; - CCSprite* gauntlet = static_cast(gauntlet_node->getChildByID("gauntlet-sprite")); - CCNode* gauntlet_shadow = gauntlet_node->getChildByID("gauntlet-shadow-sprite"); - - bg->setVisible(false); - label->setVisible(false); - label_shadow->setVisible(false); - label_gauntlet->setVisible(false); - label_shadow_gauntlet->setVisible(false); - gauntlet->setVisible(false); - gauntlet_shadow->setVisible(false); - - std::string gauntletName = label->getString(); - - auto scaleFactor = 4/CCDirector::get()->getContentScaleFactor(); - - if (gauntletName == "World") { - CCSprite* sprite = CCSprite::create("world_gauntlet.png"_spr); - sprite->setPosition(bg->getPosition()); - sprite->setScale(1.270f/scaleFactor); - sprite->setZOrder(0); - sprite->setID("gauntlet-background"_spr); - node->addChild(sprite); - - // Show gauntlets sprite again - gauntlet->setVisible(true); - gauntlet->setPosition({1, 16}); - gauntlet->setScale(1); - gauntlet_node->setZOrder(3); - - // Particle - CCParticleSystemQuad* particle = GameToolbox::particleFromString("22a-1a3a0a20a90a0a29a0a0a0a0a0a0a0a0a0a7a0a0a0a0.729412a0a0.352941a0a0.796078a0a1a0a7a0a0a0a0.729412a0a0.352941a0a0.796078a0a1a0a0.4a0a0.4a0a35a0a35a0a-50a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle); - particle->setPosition({10, 24}); - particle->setZOrder(1); - particle->setScaleX(1.310f); - particle->setScaleY(2.610f); - particle->setID("gauntlet-particles-1"_spr); - CCParticleSystemQuad* particle2 = GameToolbox::particleFromString("22a-1a3a0a20a90a0a29a0a0a0a0a0a0a0a0a0a7a0a0a0a0.34902a0a0.466667a0a0.792157a0a1a0a7a0a0a0a0.34902a0a0.466667a0a0.792157a0a1a0a0.4a0a0.4a0a35a0a35a0a-50a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle2); - particle2->setPosition({6, 24}); - particle2->setZOrder(1); - particle2->setScaleX(1.310f); - particle2->setScaleY(2.610f); - particle2->setID("gauntlet-particles-2"_spr); - CCParticleSystemQuad* particle3 = GameToolbox::particleFromString("22a-1a3a0a20a90a0a29a0a0a0a0a0a0a0a0a0a7a0a0a0a0.360784a0a0.854902a0a0.368627a0a1a0a7a0a0a0a0.360784a0a0.854902a0a0.368627a0a1a0a0.4a0a0.4a0a35a0a35a0a-50a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle3); - particle3->setPosition({2, 24}); - particle3->setZOrder(1); - particle3->setScaleX(1.310f); - particle3->setScaleY(2.610f); - particle3->setID("gauntlet-particles-3"_spr); - CCParticleSystemQuad* particle4 = GameToolbox::particleFromString("22a-1a3a0a20a90a0a29a0a0a0a0a0a0a0a0a0a7a0a0a0a0.866667a0a0.839216a0a0.247059a0a1a0a7a0a0a0a0.866667a0a0.839216a0a0.247059a0a1a0a0.4a0a0.4a0a35a0a35a0a-50a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle4); - particle4->setPosition({-2, 24}); - particle4->setZOrder(1); - particle4->setScaleX(1.310f); - particle4->setScaleY(2.610f); - particle4->setID("gauntlet-particles-4"_spr); - CCParticleSystemQuad* particle5 = GameToolbox::particleFromString("22a-1a3a0a20a90a0a29a0a0a0a0a0a0a0a0a0a7a0a0a0a0.866667a0a0.294118a0a0.247059a0a1a0a7a0a0a0a0.866667a0a0.294118a0a0.247059a0a1a0a0.4a0a0.4a0a35a0a35a0a-50a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle5); - particle5->setPosition({-6, 24}); - particle5->setZOrder(1); - particle5->setScaleX(1.310f); - particle5->setScaleY(2.610f); - particle5->setID("gauntlet-particles-5"_spr); - CCParticleSystemQuad* particle6 = GameToolbox::particleFromString("22a-1a3a0a20a90a0a29a0a0a0a0a0a0a0a0a0a7a0a0a0a0.729412a0a0.352941a0a0.796078a0a1a0a7a0a0a0a0.729412a0a0.352941a0a0.796078a0a1a0a0.4a0a0.4a0a35a0a35a0a-50a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle6); - particle6->setPosition({-9, 24}); - particle6->setZOrder(1); - particle6->setScaleX(-1.310f); - particle6->setScaleY(2.610f); - particle6->setID("gauntlet-particles-6"_spr); - CCParticleSystemQuad* particle7 = GameToolbox::particleFromString("22a-1a3a0a20a90a0a29a0a0a0a0a0a0a0a0a0a7a0a0a0a0.34902a0a0.466667a0a0.792157a0a1a0a7a0a0a0a0.34902a0a0.466667a0a0.792157a0a1a0a0.4a0a0.4a0a35a0a35a0a-50a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle7); - particle7->setPosition({-5, 24}); - particle7->setZOrder(1); - particle7->setScaleX(-1.310f); - particle7->setScaleY(2.610f); - particle7->setID("gauntlet-particles-7"_spr); - CCParticleSystemQuad* particle8 = GameToolbox::particleFromString("22a-1a3a0a20a90a0a29a0a0a0a0a0a0a0a0a0a7a0a0a0a0.360784a0a0.854902a0a0.368627a0a1a0a7a0a0a0a0.360784a0a0.854902a0a0.368627a0a1a0a0.4a0a0.4a0a35a0a35a0a-50a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle8); - particle8->setPosition({-1, 24}); - particle8->setZOrder(1); - particle8->setScaleX(-1.310f); - particle8->setScaleY(2.610f); - particle8->setID("gauntlet-particles-8"_spr); - CCParticleSystemQuad* particle9 = GameToolbox::particleFromString("22a-1a3a0a20a90a0a29a0a0a0a0a0a0a0a0a0a7a0a0a0a0.866667a0a0.839216a0a0.247059a0a1a0a7a0a0a0a0.866667a0a0.839216a0a0.247059a0a1a0a0.4a0a0.4a0a35a0a35a0a-50a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle9); - particle9->setPosition({3, 24}); - particle9->setZOrder(1); - particle9->setScaleX(-1.310f); - particle9->setScaleY(2.610f); - particle9->setID("gauntlet-particles-9"_spr); - CCParticleSystemQuad* particle10 = GameToolbox::particleFromString("22a-1a3a0a20a90a0a29a0a0a0a0a0a0a0a0a0a7a0a0a0a0.866667a0a0.294118a0a0.247059a0a1a0a7a0a0a0a0.866667a0a0.294118a0a0.247059a0a1a0a0.4a0a0.4a0a35a0a35a0a-50a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle10); - particle10->setPosition({7, 24}); - particle10->setZOrder(1); - particle10->setScaleX(-1.310f); - particle10->setScaleY(2.610f); - particle10->setID("gauntlet-particles-10"_spr); - CCParticleSystemQuad* particle11 = GameToolbox::particleFromString("38a-1a1.5a0.3a21a90a90a0a0a31a69a0a0a0a0a0a0a2a1a65a57a1a0a1a0a1a0a1a0a5a1a0a0a1a0a1a0a1a0a1a0a0.2a0a0.4a0a0a0a0a0a0a0a0a2a1a0a0a0a169a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle11); - particle11->setPosition({1, 6}); - particle11->setZOrder(1); - particle11->setScaleX(1.710f); - particle11->setScaleY(1.660f); - particle11->setID("gauntlet-particles-11"_spr); - CCParticleSystemQuad* particle12 = GameToolbox::particleFromString("11a-1a1.5a0.3a6a90a90a0a0a31a69a0a0a0a0a0a0a4a1a0a0a1a0a1a0a1a0a1a0a7a1a52a55a1a0a1a0a1a0a1a0a0.2a0a0.4a0a0a0a0a0a0a0a0a2a1a0a0a0a167a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle12); - particle12->setPosition({1, 16}); - particle12->setZOrder(1); - particle12->setScaleX(1.835f); - particle12->setScaleY(1.485f); - particle12->setID("gauntlet-particles-12"_spr); - } else if (gauntletName == "Fantasy") { - CCSprite* sprite = CCSprite::create("fantasy_gauntlet.png"_spr); - sprite->setPosition(bg->getPosition()); - sprite->setScale(1.225f/scaleFactor); - sprite->setZOrder(0); - sprite->setID("gauntlet-background"_spr); - node->addChild(sprite); - - // Show gauntlets sprite again - gauntlet->setVisible(true); - gauntlet->setPosition({-2, 14}); - gauntlet->setScale(0.925f); - gauntlet_node->setZOrder(2); - - // Particle - CCParticleSystemQuad* particle = GameToolbox::particleFromString("26a-1a5a0.3a4a-90a14a9a0a17a0a0a0a0a0a0a0a2a1a76a325a0.537255a0a0.898039a0a0.164706a0a1a0a2a1a148a149a0.537255a0a0.898039a0a0.164706a0a1a0a0a0a0.3a0a0a0a0a0a0a0a0a2a0a0a0a0a146a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle); - particle->setPosition({gauntlet->getPositionX(), 103}); - particle->setZOrder(1); - particle->setScaleX(2.880f); - particle->setScaleY(4.603f); - particle->setID("gauntlet-particles-1"_spr); - } else if (gauntletName == "Portal") { - CCSprite* sprite = CCSprite::create("portal_gauntlet.png"_spr); - sprite->setPosition(bg->getPosition()); - sprite->setScale(1.225f/scaleFactor); - sprite->setZOrder(0); - sprite->setID("gauntlet-background"_spr); - node->addChild(sprite); - - // Show gauntlets sprite again - gauntlet->setVisible(true); - gauntlet->setPosition({-2, 14}); - gauntlet->setScale(0.925f); - gauntlet_node->setZOrder(2); - - // Particle - CCParticleSystemQuad* particle = GameToolbox::particleFromString("13a-1a1a0.3a10a90a90a29a0a11a0a0a0a0a0a0a0a50a1a0a37a0.376471a0a0.109804a0a0.243137a0a1a0a32a1a142a96a0.376471a0a0.109804a0a0.243137a0a1a0a0.5a0a0.3a0a0a0a0a0a0a0a1a2a1a0a0a0a190a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle); - particle->setPosition({gauntlet->getPositionX(), 14}); - particle->setZOrder(1); - particle->setScaleX(2.880f); - particle->setScaleY(3.978f); - particle->setID("gauntlet-particles-1"_spr); - CCParticleSystemQuad* particle2 = GameToolbox::particleFromString("13a-1a1a0.3a10a90a90a29a0a11a0a0a0a0a0a0a0a50a1a0a37a0.0705882a0a0.172549a0a0.203922a0a1a0a32a1a142a96a0.0705882a0a0.172549a0a0.203922a0a1a0a0.5a0a0.3a0a0a0a0a0a0a0a1a2a1a0a0a0a190a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle2); - particle2->setPosition({gauntlet->getPositionX(), 14}); - particle2->setZOrder(1); - particle2->setScaleX(2.880f); - particle2->setScaleY(3.978f); - particle2->setID("gauntlet-particles-2"_spr); - CCParticleSystemQuad* particle3 = GameToolbox::particleFromString("38a-1a2a0.3a16a90a180a29a0a11a0a0a0a0a0a0a0a6a1a0a0a0.760784a0a0.380392a0a0.835294a0a1a0a3a1a0a0a0.760784a0a0.380392a0a0.835294a0a1a0a0.18a0a0.49a0a43a0a33a0a-34a0a1a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle3); - particle3->setPosition({gauntlet->getPositionX(), 14}); - particle3->setZOrder(1); - particle3->setScaleX(1.030f); - particle3->setScaleY(1.030f); - particle3->setID("gauntlet-particles-3"_spr); - CCParticleSystemQuad* particle4 = GameToolbox::particleFromString("38a-1a2a0.3a16a90a180a29a0a11a0a0a0a0a0a0a0a6a1a0a0a0.376471a0a0.690196a0a0.831373a0a1a0a3a1a0a0a0.376471a0a0.690196a0a0.831373a0a1a0a0.18a0a0.49a0a43a0a33a0a-34a0a1a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle4); - particle4->setPosition({gauntlet->getPositionX(), 14}); - particle4->setZOrder(1); - particle4->setScaleX(1.155f); - particle4->setScaleY(1.355f); - particle4->setID("gauntlet-particles-4"_spr); - CCParticleSystemQuad* particle5 = GameToolbox::particleFromString("30a-1a1a0.3a30a90a180a29a0a11a0a0a0a0a0a0a0a4a1a0a0a0.760784a0a0.380392a0a0.835294a0a1a0a2a1a0a0a0.760784a0a0.380392a0a0.835294a0a1a0a0.18a0a0.49a0a43a0a33a0a-41a0a1a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle5); - particle5->setPosition({gauntlet->getPositionX(), 14}); - particle5->setZOrder(1); - particle5->setScaleX(1.455f); - particle5->setScaleY(1.655f); - particle5->setID("gauntlet-particles-5"_spr); - CCParticleSystemQuad* particle6 = GameToolbox::particleFromString("30a-1a1a0.3a30a90a180a29a0a11a0a0a0a0a0a0a0a4a1a0a0a0.376471a0a0.690196a0a0.831373a0a1a0a2a1a0a0a0.376471a0a0.690196a0a0.831373a0a1a0a0.18a0a0.49a0a43a0a33a0a-41a0a1a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle6); - particle6->setPosition({gauntlet->getPositionX(), 14}); - particle6->setZOrder(1); - particle6->setScaleX(1.355f); - particle6->setScaleY(1.855f); - particle6->setID("gauntlet-particles-6"_spr); - } else if (gauntletName == "Demon") { - CCSprite* sprite = CCSprite::create("demon_gauntlet.png"_spr); - sprite->setPosition(bg->getPosition()); - sprite->setScale(1.275f/scaleFactor); - sprite->setZOrder(0); - sprite->setID("gauntlet-background"_spr); - node->addChild(sprite); - - // Show gauntlets sprite again - gauntlet->setVisible(true); - gauntlet->setPosition({-1, 22}); - gauntlet->setScale(1.1f); - gauntlet_node->setZOrder(2); - - // Particle - CCParticleSystemQuad* particle = GameToolbox::particleFromString("66a-1a3a0.3a20a90a27a11a0a37a38a0a0a0a0a0a0a5a1a0a0a0.694118a0a0.45098a0a0.054902a0a1a0a2a1a0a0a0.509804a0a0.172549a0a0a0a1a0a0.5a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle); - particle->setPosition(gauntlet->getPosition()); - particle->setZOrder(1); - particle->setID("gauntlet-particles-1"_spr); - CCParticleSystemQuad* particle2 = GameToolbox::particleFromString("29a-1a3a0.3a8a90a27a11a0a37a38a0a0a0a0a0a0a50a1a0a0a0.0509804a0a0.0392157a0a0.0352941a0a1a0a50a1a0a0a0.0509804a0a0.0392157a0a0.0352941a0a1a0a0.5a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle2); - particle2->setPosition(gauntlet->getPosition()); - particle2->setZOrder(1); - particle2->setID("gauntlet-particles-2"_spr); - CCParticleSystemQuad* particle3 = GameToolbox::particleFromString("31a-1a1a0.3a23a90a90a29a0a11a0a0a0a0a0a0a0a33a1a89a149a0.454902a0a0.321569a0a0.152941a0a1a0a50a1a145a68a0.454902a0a0.321569a0a0.152941a0a1a0a0.5a0a0.5a0a0a0a0a0a0a0a1a2a1a0a0a0a192a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle3); - particle3->setPosition(gauntlet->getPosition()); - particle3->setZOrder(1); - particle3->setID("gauntlet-particles-3"_spr); - CCParticleSystemQuad* particle4 = GameToolbox::particleFromString("30a-1a2a0.3a13a90a8a21a0a8a0a0a45a0a0a0a0a40a1a89a149a0.396078a0a0.27451a0a0.14902a0a1a0a24a1a145a68a0.141176a0a0.0352941a0a0.0313726a0a1a0a0.5a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle4); - particle4->setPosition(gauntlet->getPosition()); - particle4->setZOrder(1); - particle4->setID("gauntlet-particles-4"_spr); - CCParticleSystemQuad* particle5 = GameToolbox::particleFromString("30a-1a1a0.3a13a90a8a21a0a0a0a0a45a0a0a0a0a21a1a89a149a0.894118a0a0.631373a0a0.345098a0a1a0a6a1a145a68a0.894118a0a0.631373a0a0.345098a0a1a0a0.5a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a183a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle5); - particle5->setPosition(gauntlet->getPosition()); - particle5->setZOrder(1); - particle5->setID("gauntlet-particles-5"_spr); - } else if (gauntletName == "Castle") { - CCSprite* sprite = CCSprite::create("castle_gauntlet.png"_spr); - sprite->setPosition(bg->getPosition()); - sprite->setScale(1.275f/scaleFactor); - sprite->setZOrder(0); - sprite->setID("gauntlet-background"_spr); - node->addChild(sprite); - } else if (gauntletName == "Galaxy") { - CCSprite* sprite = CCSprite::create("galaxy_gauntlet.png"_spr); - sprite->setPosition(bg->getPosition()); - sprite->setScale(1.695f/scaleFactor); - sprite->setZOrder(0); - sprite->setID("gauntlet-background"_spr); - node->addChild(sprite); - - // Show gauntlets sprite again - gauntlet->setVisible(true); - gauntlet->setPosition({0, 11}); - gauntlet->setScale(0.975); - gauntlet_node->setZOrder(3); - - // Particle - CCParticleSystemQuad* particle = GameToolbox::particleFromString("11a-1a1a0.3a8a90a90a0a0a0a0a0a0a0a0a0a0a50a3a0a0a0.627451a0a0.286275a0a0.470588a0a1a0a50a3a106a70a0.627451a0a0.286275a0a0.470588a0a1a0a0a0a0a0a0a0a0a0a0a0a0a2a1a0a0a0a176a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle); - particle->setPosition({0, 24}); - particle->setZOrder(1); - particle->setScaleX(1.610f); - particle->setScaleY(1.610f); - particle->setID("gauntlet-particles-1"_spr); - CCParticleSystemQuad* particle2 = GameToolbox::particleFromString("14a-1a1.2a0.3a9a112a6a42a0a15a0a0a0a0a0a0a0a4a0a340a0a0.913725a0a0.72549a0a0.337255a0a1a0a4a0a340a0a0.913725a0a0.72549a0a0.337255a0a1a0a0.3a0a0.3a0a0a0a0a0a0a0a0a2a1a0a0a0a31a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle2); - particle2->setPosition({-25, 111}); - particle2->setZOrder(1); - particle2->setScaleX(2.660f); - particle2->setScaleY(3.710f); - particle2->setRotation(180); - particle2->setID("gauntlet-particles-2"_spr); - CCParticleSystemQuad* particle3 = GameToolbox::particleFromString("38a-1a1.5a0.3a21a90a90a0a0a31a69a0a0a0a0a0a0a2a1a65a57a0.886275a0a0.866667a0a0.454902a0a1a0a5a1a0a0a0.886275a0a0.866667a0a0.454902a0a1a0a0.2a0a0.4a0a0a0a0a0a0a0a0a2a1a0a0a0a169a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle3); - particle3->setPosition({2, 6}); - particle3->setZOrder(1); - particle3->setScaleX(1.685f); - particle3->setScaleY(1.485f); - particle3->setID("gauntlet-particles-3"_spr); - CCParticleSystemQuad* particle4 = GameToolbox::particleFromString("11a-1a1.5a0.3a6a90a90a0a0a31a69a0a0a0a0a0a0a4a1a0a0a0.890196a0a0.376471a0a0.65098a0a1a0a7a1a52a55a0.890196a0a0.376471a0a0.65098a0a1a0a0.2a0a0.4a0a0a0a0a0a0a0a0a2a1a0a0a0a167a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle4); - particle4->setPosition({2, -2}); - particle4->setZOrder(1); - particle4->setScaleX(1.310f); - particle4->setScaleY(1.635f); - particle4->setID("gauntlet-particles-4"_spr); - CCParticleSystemQuad* particle5 = GameToolbox::particleFromString("4a-1a1a0.3a3a90a90a0a0a0a0a0a0a0a0a0a0a50a3a0a0a0.862745a0a0.352941a0a0.623529a0a1a0a50a3a106a22a0.862745a0a0.352941a0a0.623529a0a1a0a0.3a0a0.3a0a0a0a0a0a0a0a0a2a1a0a0a0a164a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle5); - particle5->setPosition({0, 24}); - particle5->setZOrder(1); - particle5->setScaleX(1.785f); - particle5->setScaleY(1.785f); - particle5->setID("gauntlet-particles-5"_spr); - } else if (gauntletName == "Universe") { - CCSprite* sprite = CCSprite::create("universe_gauntlet.png"_spr); - sprite->setPosition(bg->getPosition()); - sprite->setScale(1.270f/scaleFactor); - sprite->setZOrder(0); - sprite->setID("gauntlet-background"_spr); - node->addChild(sprite); - - // Show gauntlets sprite again - gauntlet->setVisible(true); - gauntlet->setPosition({0, 16}); - gauntlet->setScale(0.975f); - gauntlet_node->setZOrder(3); - - // Particle - CCParticleSystemQuad* particle = GameToolbox::particleFromString("23a-1a3a0.3a6a90a180a29a0a11a0a0a0a0a0a0a0a38a1a0a0a0.0627451a0a0.0980392a0a0.105882a0a1a0a50a1a0a0a0.0627451a0a0.0980392a0a0.105882a0a1a0a0.1a0a0.5a0a10a0a28a0a0a0a1a2a1a0a1a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle); - particle->setPosition({0, 11}); - particle->setZOrder(1); - particle->setScaleX(1.485f); - particle->setScaleY(2.685f); - particle->setID("gauntlet-particles-1"_spr); - CCParticleSystemQuad* particle2 = GameToolbox::particleFromString("20a-1a0.5a0a15a90a0a44a0a11a0a0a0a0a0a0a0a4a1a0a0a0.760784a0a0.447059a0a0.94902a0a1a0a4a1a0a0a0.760784a0a0.447059a0a0.94902a0a1a0a0.1a0a0.1a0a0a0a0a0a0a0a0a2a1a0a0a0a33a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle2); - particle2->setPosition({32, -74}); - particle2->setZOrder(1); - particle2->setScaleX(3.685f); - particle2->setScaleY(8.310f); - particle2->setRotation(-20); - particle2->setID("gauntlet-particles-2"_spr); - CCParticleSystemQuad* particle3 = GameToolbox::particleFromString("19a-1a0.5a0a38a90a0a44a0a20a0a0a0a0a0a0a0a2a1a0a0a0.94902a0a0.67451a0a0.447059a0a1a0a2a1a0a0a0.94902a0a0.67451a0a0.447059a0a1a0a0.1a0a0.1a0a0a0a0a0a0a0a0a2a1a0a0a0a33a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle3); - particle3->setPosition({34, -96}); - particle3->setZOrder(1); - particle3->setScaleX(2.135f); - particle3->setScaleY(8.935f); - particle3->setRotation(-20); - particle3->setID("gauntlet-particles-3"_spr); - CCParticleSystemQuad* particle4 = GameToolbox::particleFromString("20a-1a1a0a15a90a0a36a0a11a0a0a0a0a0a0a0a4a1a0a0a0.447059a0a0.654902a0a0.94902a0a1a0a4a1a0a0a0.447059a0a0.654902a0a0.94902a0a1a0a0.2a0a0.2a0a0a0a0a0a0a0a0a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle4); - particle4->setPosition({41, -92}); - particle4->setZOrder(1); - particle4->setScaleX(3.235f); - particle4->setScaleY(6.585f); - particle4->setRotation(-20); - particle4->setID("gauntlet-particles-4"_spr); - CCParticleSystemQuad* particle5 = GameToolbox::particleFromString("11a-1a1a0.3a8a90a90a0a0a0a0a0a0a0a0a0a0a50a3a0a0a0.239216a0a0.403922a0a0.419608a0a1a0a50a3a106a70a0.239216a0a0.403922a0a0.419608a0a1a0a0a0a0a0a0a0a0a0a0a0a0a2a1a0a0a0a176a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle5); - particle5->setPosition({0, 24}); - particle5->setZOrder(1); - particle5->setScaleX(1.710f); - particle5->setScaleY(1.710f); - particle5->setID("gauntlet-particles-5"_spr); - CCParticleSystemQuad* particle6 = GameToolbox::particleFromString("4a-1a1a0.3a3a90a90a0a0a0a0a0a0a0a0a0a0a50a3a0a0a0.32549a0a0.537255a0a0.560784a0a1a0a50a3a106a22a0.32549a0a0.537255a0a0.560784a0a1a0a0.3a0a0.3a0a0a0a0a0a0a0a0a2a1a0a0a0a190a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle6); - particle6->setPosition({-2, 24}); - particle6->setZOrder(1); - particle6->setScaleX(2.710f); - particle6->setScaleY(2.710f); - particle6->setID("gauntlet-particles-6"_spr); - } else if (gauntletName == "Cursed") { - CCSprite* sprite = CCSprite::create("cursed_gauntlet.png"_spr); - sprite->setPosition(bg->getPosition()); - sprite->setScale(1.270f/scaleFactor); - sprite->setZOrder(0); - sprite->setID("gauntlet-background"_spr); - node->addChild(sprite); - - // Show gauntlets sprite again - gauntlet->setVisible(true); - gauntlet->setPosition({1, 16}); - gauntlet->setScale(1); - gauntlet_node->setZOrder(3); - - // Particle - CCParticleSystemQuad* particle = GameToolbox::particleFromString("5a-1a1a0.3a3a90a90a29a0a11a0a0a0a0a0a0a0a50a1a24a45a0.105882a0a0.380392a0a0.105882a0a1a0a44a1a48a28a0.105882a0a0.380392a0a0.105882a0a1a0a0a0a0a0a0a0a0a0a0a0a1a2a1a0a0a0a175a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle); - particle->setPosition(gauntlet->getPosition()); - particle->setZOrder(1); - particle->setScaleX(2.035f); - particle->setScaleY(2.035f); - particle->setID("gauntlet-particles-1"_spr); - CCParticleSystemQuad* particle2 = GameToolbox::particleFromString("5a-1a1a0.3a3a90a90a29a0a11a0a0a0a0a0a0a0a50a1a24a45a0.105882a0a0.380392a0a0.105882a0a1a0a44a1a48a28a0.105882a0a0.380392a0a0.105882a0a1a0a0a0a0a0a0a0a0a0a0a0a1a2a1a0a0a0a175a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle2); - particle2->setPosition(gauntlet->getPosition()); - particle2->setZOrder(1); - particle2->setScaleX(2.035f); - particle2->setScaleY(2.035f); - particle2->setID("gauntlet-particles-2"_spr); - CCParticleSystemQuad* particle3 = GameToolbox::particleFromString("93a-1a1a0.3a71a90a0a52a0a10a0a23a0a0a0a0a0a2a1a0a0a0.00392157a0a0.905882a0a0a0a1a0a2a1a30a0a0.00392157a0a0.905882a0a0a0a1a0a0.3a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a32a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle3); - particle3->setPosition({17, 94}); - particle3->setZOrder(1); - particle3->setScaleX(4.510f); - particle3->setScaleY(3.335f); - particle3->setRotation(180); - particle3->setID("gauntlet-particles-3"_spr); - } else if (gauntletName == "Doom") { - CCSprite* sprite = CCSprite::create("doom_gauntlet.png"_spr); - sprite->setPosition(bg->getPosition()); - sprite->setScale(1.270f/scaleFactor); - sprite->setZOrder(0); - sprite->setID("gauntlet-background"_spr); - node->addChild(sprite); - - // Show gauntlets sprite again - gauntlet->setVisible(true); - gauntlet->setPosition({0, 19}); - gauntlet->setScale(1.025f); - gauntlet_node->setZOrder(3); - - // Particle - CCParticleSystemQuad* particle = GameToolbox::particleFromString("66a-1a3a0.3a20a90a27a11a0a43a83a0a0a0a0a0a0a5a1a0a0a0.733333a0a0.607843a0a0.12549a0a1a0a3a1a0a0a0.733333a0a0.470588a0a0.12549a0a1a0a0.5a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle); - particle->setPosition({0, -7}); - particle->setZOrder(1); - particle->setScaleX(1.235f); - particle->setScaleY(1.235f); - particle->setID("gauntlet-particles-1"_spr); - CCParticleSystemQuad* particle2 = GameToolbox::particleFromString("44a-1a3a0.3a13a90a27a11a0a32a62a0a0a0a0a0a0a50a1a0a0a0.0666667a0a0.0509804a0a0.0470588a0a1a0a50a1a0a0a0.0666667a0a0.0509804a0a0.0470588a0a1a0a0.5a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle2); - particle2->setPosition({0, -13}); - particle2->setZOrder(1); - particle2->setScaleX(1.310f); - particle2->setScaleY(1.435f); - particle2->setID("gauntlet-particles-2"_spr); - CCParticleSystemQuad* particle3 = GameToolbox::particleFromString("6a-1a1.9a0a3a90a17a69a0a3a0a0a-102a0a0a0a0a4a1a57a105a1a0a1a0a1a0a1a0a4a1a0a0a1a0a1a0a1a0a1a0a0a0a0a0a0a0a0a0a0a0a0a2a1a0a0a0a145a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle3); - particle3->setPosition(gauntlet->getPosition()); - particle3->setZOrder(1); - particle3->setScaleX(2.035f); - particle3->setScaleY(2.035f); - particle3->setID("gauntlet-particles-3"_spr); - } else if (gauntletName == "Split") { - CCSprite* sprite = CCSprite::create("split_gauntlet.png"_spr); - sprite->setPosition(bg->getPosition()); - sprite->setScale(1.270f/scaleFactor); - sprite->setZOrder(0); - sprite->setID("gauntlet-background"_spr); - node->addChild(sprite); - - // Show gauntlets sprite again - gauntlet->setVisible(true); - gauntlet->setPosition({-1, 20}); - gauntlet->setScale(0.975f); - gauntlet_node->setZOrder(2); - - // Particle - CCParticleSystemQuad* particle = GameToolbox::particleFromString("48a-1a2a0.3a20a90a2a19a0a10a20a0a0a0a0a0a0a4a1a0a0a0.666667a0a0.360784a0a0.603922a0a1a0a2a1a0a0a0.666667a0a0.360784a0a0.603922a0a1a0a0.5a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a31a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle); - particle->setPosition({-28, -65}); - particle->setZOrder(1); - particle->setScaleX(2.180f); - particle->setScaleY(4.003f); - particle->setID("gauntlet-particles-1"_spr); - CCParticleSystemQuad* particle2 = GameToolbox::particleFromString("48a-1a2a0.3a20a90a2a19a0a10a20a0a0a0a0a0a0a4a1a0a0a0.454902a0a0.215686a0a0.403922a0a1a0a2a1a0a0a0.454902a0a0.215686a0a0.403922a0a1a0a0.5a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle2); - particle2->setPosition({-28, -65}); - particle2->setZOrder(1); - particle2->setScaleX(2.180f); - particle2->setScaleY(4.003f); - particle2->setID("gauntlet-particles-2"_spr); - CCParticleSystemQuad* particle3 = GameToolbox::particleFromString("48a-1a2a0.3a20a90a2a19a0a10a20a0a0a0a0a0a0a4a1a0a0a0.243137a0a0.329412a0a0.341176a0a1a0a2a1a0a0a0.243137a0a0.329412a0a0.341176a0a1a0a0.5a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a31a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle3); - particle3->setPosition({25, 65}); - particle3->setZOrder(1); - particle3->setScaleX(2.180f); - particle3->setScaleY(4.003f); - particle3->setRotation(180); - particle3->setID("gauntlet-particles-3"_spr); - CCParticleSystemQuad* particle4 = GameToolbox::particleFromString("48a-1a2a0.3a20a90a2a19a0a10a20a0a0a0a0a0a0a4a1a0a0a0.145098a0a0.203922a0a0.207843a0a1a0a2a1a0a0a0.145098a0a0.203922a0a0.207843a0a1a0a0.5a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle4); - particle4->setPosition({25, 65}); - particle4->setZOrder(1); - particle4->setScaleX(2.180f); - particle4->setScaleY(4.003f); - particle4->setRotation(180); - particle4->setID("gauntlet-particles-4"_spr); - CCParticleSystemQuad* particle5 = GameToolbox::particleFromString("30a-1a2a0.3a13a90a90a29a0a11a0a0a0a0a0a0a0a6a1a0a0a0.705882a0a0.34902a0a0.698039a0a1a0a2a1a0a0a0.584314a0a0.34902a0a0.705882a0a1a0a0a0a0.5a0a6a0a34a0a0a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle5); - particle5->setPosition({0, 29}); - particle5->setZOrder(1); - particle5->setScaleX(1.955f); - particle5->setScaleY(1.955f); - particle5->setRotation(90); - particle5->setID("gauntlet-particles-5"_spr); - CCParticleSystemQuad* particle6 = GameToolbox::particleFromString("30a-1a2a0.3a13a90a90a29a0a11a0a0a0a0a0a0a0a6a1a0a0a0.254902a0a0.333333a0a0.388235a0a1a0a2a1a0a0a0.254902a0a0.333333a0a0.388235a0a1a0a0a0a0.5a0a6a0a34a0a0a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle6); - particle6->setPosition({0, 29}); - particle6->setZOrder(1); - particle6->setScaleX(1.955f); - particle6->setScaleY(1.955f); - particle6->setRotation(-90); - particle6->setID("gauntlet-particles-6"_spr); - CCParticleSystemQuad* particle7 = GameToolbox::particleFromString("30a-1a2a0.3a13a90a90a29a0a11a0a0a0a0a0a0a0a6a1a0a0a0.705882a0a0.34902a0a0.698039a0a1a0a2a1a0a0a0.584314a0a0.34902a0a0.705882a0a1a0a0a0a0.5a0a6a0a34a0a0a0a1a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle7); - particle7->setPosition({0, 29}); - particle7->setZOrder(1); - particle7->setScaleX(1.955f); - particle7->setScaleY(1.955f); - particle7->setRotation(90); - particle7->setID("gauntlet-particles-7"_spr); - CCParticleSystemQuad* particle8 = GameToolbox::particleFromString("30a-1a2a0.3a13a90a90a29a0a11a0a0a0a0a0a0a0a6a1a0a0a0.254902a0a0.333333a0a0.388235a0a1a0a2a1a0a0a0.254902a0a0.333333a0a0.388235a0a1a0a0a0a0.5a0a6a0a34a0a0a0a1a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle8); - particle8->setPosition({0, 29}); - particle8->setZOrder(1); - particle8->setScaleX(1.955f); - particle8->setScaleY(1.955f); - particle8->setRotation(-90); - particle8->setID("gauntlet-particles-8"_spr); - } else if (gauntletName == "Monster") { - CCSprite* sprite = CCSprite::create("monster_gauntlet.png"_spr); - sprite->setPosition(bg->getPosition()); - sprite->setScale(1.270f/scaleFactor); - sprite->setZOrder(0); - sprite->setID("gauntlet-background"_spr); - node->addChild(sprite); - - // Show gauntlets sprite again - gauntlet->setVisible(true); - gauntlet->setPosition({0, 22}); - gauntlet->setScale(1.075f); - gauntlet_node->setZOrder(2); - - // Particle - CCParticleSystemQuad* particle = GameToolbox::particleFromString("30a-1a3a0.3a9a90a9a11a0a17a40a0a0a0a0a0a0a4a1a0a0a0.0509804a0a0.929412a0a0a0a1a0a2a1a0a0a0.360784a0a0.486275a0a0.352941a0a1a0a0.1a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a165a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle); - particle->setPosition({gauntlet->getPositionX(), -37}); - particle->setZOrder(1); - particle->setScaleX(2.510f); - particle->setScaleY(1.985f); - particle->setID("gauntlet-particles-1"_spr); - CCParticleSystemQuad* particle2 = GameToolbox::particleFromString("66a-1a3a0.3a20a90a27a11a0a37a38a0a0a0a0a0a0a5a1a0a0a0.0156863a0a0.866667a0a0.027451a0a1a0a3a1a0a0a0.164706a0a0.509804a0a0a0a1a0a0.5a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle2); - particle2->setPosition({gauntlet->getPositionX(), -38}); - particle2->setZOrder(1); - particle2->setScaleX(1.072f); - particle2->setScaleY(2.475f); - particle2->setID("gauntlet-particles-2"_spr); - CCParticleSystemQuad* particle3 = GameToolbox::particleFromString("18a-1a3a0.3a5a90a9a11a0a32a28a0a0a0a0a0a0a50a1a0a0a0.0352941a0a0.0901961a0a0.0313726a0a1a0a50a1a0a0a0.0352941a0a0.0901961a0a0.0313726a0a1a0a1a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle3); - particle3->setPosition({gauntlet->getPositionX(), -48}); - particle3->setZOrder(1); - particle3->setScaleX(1.470f); - particle3->setScaleY(1.957f); - particle3->setID("gauntlet-particles-3"_spr); - } else if (gauntletName == "Mystery") { - CCSprite* sprite = CCSprite::create("mystery_gauntlet.png"_spr); - sprite->setPosition(bg->getPosition()); - sprite->setScale(1.270f/scaleFactor); - sprite->setZOrder(0); - sprite->setID("gauntlet-background"_spr); - node->addChild(sprite); - - // Show gauntlets sprite again - gauntlet->setVisible(true); - gauntlet->setPosition({3, 13}); - gauntlet->setScale(0.975f); - gauntlet_node->setZOrder(3); - - // Particle - CCParticleSystemQuad* particle = GameToolbox::particleFromString("30a-1a3a0.3a9a90a180a29a0a11a0a0a0a0a0a0a0a2a1a0a0a1a0a0.8a0a0.258824a0a1a0a8a1a0a0a1a0a0.8a0a0.258824a0a1a0a0.5a0a0.5a0a6a0a30a0a0a0a1a2a0a0a0a0a11a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle); - particle->setPosition(gauntlet->getPosition()); - particle->setZOrder(2); - particle->setScaleX(2.035f); - particle->setScaleY(2.035f); - particle->setID("gauntlet-particles-1"_spr); - CCParticleSystemQuad* particle2 = GameToolbox::particleFromString("31a-1a3a0.3a9a90a180a0a0a29a82a0a0a0a0a0a0a4a1a0a0a0.388235a0a0.741176a0a1a0a1a0a15a1a0a0a0.388235a0a0.741176a0a1a0a1a0a1a0a1a0a6a0a30a0a0a0a0a2a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle2); - particle2->setPosition({0, 0}); - particle2->setZOrder(1); - particle2->setScaleX(1.435f); - particle2->setScaleY(1.435f); - particle2->setID("gauntlet-particles-2"_spr); - } else if (gauntletName == "Strange") { - CCSprite* sprite = CCSprite::create("strange_gauntlet.png"_spr); - sprite->setPosition(bg->getPosition()); - sprite->setScale(1.270f/scaleFactor); - sprite->setZOrder(0); - sprite->setID("gauntlet-background"_spr); - node->addChild(sprite); - - reverseGauntlet(node); - - // Show gauntlets sprite again - gauntlet->setVisible(true); - gauntlet->setPosition({2, -18}); - gauntlet->setRotation(180); - gauntlet->setScale(0.975f); - gauntlet->setFlipX(true); - gauntlet_node->setZOrder(2); - - // Particle - CCParticleSystemQuad* particle = GameToolbox::particleFromString("36a-1a3a0.3a6a-90a24a9a0a11a0a0a-7a0a0a0a0a2a1a76a325a0.592157a0a0.294118a0a0.658824a0a1a0a2a1a201a149a0.592157a0a0.294118a0a0.658824a0a1a0a0.3a0a0.3a0a0a0a0a0a0a0a0a2a0a0a0a0a96a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle); - particle->setPosition({2, -65}); - particle->setZOrder(2); - particle->setScaleX(2.480f); - particle->setScaleY(2.728f); - particle->setRotation(180); - particle->setID("gauntlet-particles-1"_spr); - CCParticleSystemQuad* particle2 = GameToolbox::particleFromString("17a-1a4a0.3a3a180a0a8a0a47a34a0a0a0a0a0a0a50a1a0a0a0.243137a0a0.239216a0a0.239216a0a1a0a50a1a0a0a0.454902a0a0.447059a0a0.447059a0a1a0a1a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle2); - particle2->setPosition({17, 60}); - particle2->setZOrder(3); - particle2->setScaleX(0.805f); - particle2->setScaleY(1.155f); - particle2->setID("gauntlet-particles-2"_spr); - } else if (gauntletName == "Water") { - CCSprite* sprite = CCSprite::create("water_gauntlet.png"_spr); - sprite->setPosition(bg->getPosition()); - sprite->setScale(1.270f/scaleFactor); - sprite->setZOrder(0); - sprite->setID("gauntlet-background"_spr); - node->addChild(sprite); - } else if (gauntletName == "Fire") { - CCSprite* sprite = CCSprite::create("fire_gauntlet.png"_spr); - sprite->setPosition(bg->getPosition()); - sprite->setScale(1.270f/scaleFactor); - sprite->setZOrder(0); - sprite->setID("gauntlet-background"_spr); - node->addChild(sprite); - - // Show gauntlets sprite again - gauntlet->setVisible(true); - gauntlet->setPosition({0, 15}); - gauntlet->setScale(1.050f); - gauntlet_node->setZOrder(2); - - // Particle - CCParticleSystemQuad* particle = GameToolbox::particleFromString("30a-1a2a0.3a13a90a8a21a0a8a0a0a45a0a0a0a0a50a1a89a149a0.396078a0a0.27451a0a0.14902a0a1a0a31a1a145a68a0.141176a0a0.0352941a0a0.0313726a0a1a0a0.5a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle); - particle->setPosition({-1, 0}); - particle->setZOrder(1); - particle->setScaleX(1.435f); - particle->setScaleY(0.935f); - particle->setID("gauntlet-particles-1"_spr); - CCParticleSystemQuad* particle2 = GameToolbox::particleFromString("30a-1a1a0.3a13a90a8a21a0a0a0a0a45a0a0a0a0a21a1a89a149a0.894118a0a0.631373a0a0.345098a0a1a0a13a1a145a68a0.894118a0a0.631373a0a0.345098a0a1a0a0.5a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a183a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle2); - particle2->setPosition(gauntlet->getPosition()); - particle2->setZOrder(1); - particle2->setScaleX(2.322f); - particle2->setScaleY(1.347f); - particle2->setID("gauntlet-particles-2"_spr); - CCParticleSystemQuad* particle3 = GameToolbox::particleFromString("19a-1a2a0.3a8a90a0a29a0a5a0a0a0a0a0a0a0a50a1a50a33a0.12549a0a0.0862745a0a0a0a1a0a30a1a50a30a0.12549a0a0.0862745a0a0a0a1a0a0.1a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a181a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle3); - particle3->setPosition(gauntlet->getPosition()); - particle3->setZOrder(1); - particle3->setScaleX(1.145f); - particle3->setScaleY(1.795f); - particle3->setID("gauntlet-particles-3"_spr); - CCParticleSystemQuad* particle4 = GameToolbox::particleFromString("31a-1a3a0.3a9a90a27a11a0a37a38a0a0a0a0a0a0a50a1a0a0a0.133333a0a0.0862745a0a0.0588235a0a1a0a50a1a0a0a0.0666667a0a0.0470588a0a0.0352941a0a1a0a0.5a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle4); - particle4->setPosition({1, -23}); - particle4->setZOrder(1); - particle4->setScaleX(1.270f); - particle4->setScaleY(1.832f); - particle4->setID("gauntlet-particles-4"_spr); - CCParticleSystemQuad* particle5 = GameToolbox::particleFromString("40a-1a3a0.3a12a90a13a11a0a48a78a0a0a0a0a0a0a5a3a0a0a0.439216a0a0.309804a0a0.129412a0a1a0a3a3a0a0a0.439216a0a0.309804a0a0.129412a0a1a0a0.1a0a0.3a0a0a0a0a0a0a0a0a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle5); - particle5->setPosition({2, -20}); - particle5->setZOrder(1); - particle5->setScaleX(1.205f); - particle5->setScaleY(1.280f); - particle5->setID("gauntlet-particles-5"_spr); - } else if (gauntletName == "Ice") { - CCSprite* sprite = CCSprite::create("ice_gauntlet.png"_spr); - sprite->setPosition(bg->getPosition()); - sprite->setScale(1.270f/scaleFactor); - sprite->setZOrder(0); - sprite->setID("gauntlet-background"_spr); - node->addChild(sprite); - - // Show gauntlets sprite again - gauntlet->setVisible(true); - gauntlet->setPosition({0, 15}); - gauntlet->setScale(1.050f); - gauntlet_node->setZOrder(2); - - // Particle - CCParticleSystemQuad* particle = GameToolbox::particleFromString("25a-1a1a0.3a19a90a90a0a0a0a0a0a0a0a0a0a0a12a1a0a98a0.0980392a0a0.176471a0a0.219608a0a1a0a36a1a0a0a0.0980392a0a0.176471a0a0.219608a0a1a0a0a0a0a0a0a0a0a0a0a0a0a2a1a0a0a0a191a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle); - particle->setPosition({0, 17}); - particle->setZOrder(1); - particle->setScaleX(3.760f); - particle->setScaleY(3.760f); - particle->setID("gauntlet-particles-1"_spr); - CCParticleSystemQuad* particle2 = GameToolbox::particleFromString("200a-1a4a0.3a46a90a0a18a0a24a0a0a0a0a0a0a0a1a1a0a39a0.980392a0a1a0a0.980392a0a1a0a1a1a98a41a0.980392a0a1a0a0.980392a0a1a0a0.3a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a14a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle2); - particle2->setPosition({11, 112}); - particle2->setZOrder(1); - particle2->setScaleX(2.322f); - particle2->setScaleY(3.097f); - particle2->setRotation(-174); - particle2->setID("gauntlet-particles-2"_spr); - CCParticleSystemQuad* particle3 = GameToolbox::particleFromString("44a-1a3a0.3a13a90a27a11a0a40a62a0a0a0a0a0a0a50a1a0a0a0.0431373a0a0.0627451a0a0.0666667a0a1a0a50a1a0a0a0.0431373a0a0.0627451a0a0.0666667a0a1a0a0.5a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle3); - particle3->setPosition({0, -11}); - particle3->setZOrder(1); - particle3->setScaleX(1.145f); - particle3->setScaleY(1.795f); - particle3->setID("gauntlet-particles-3"_spr); - CCParticleSystemQuad* particle4 = GameToolbox::particleFromString("38a-1a1.5a0.3a21a90a90a0a0a31a69a0a0a0a0a0a0a2a1a65a57a1a0a1a0a1a0a1a0a5a1a0a0a1a0a1a0a1a0a1a0a0.2a0a0.4a0a0a0a0a0a0a0a0a2a1a0a0a0a169a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle4); - particle4->setPosition(gauntlet->getPosition()); - particle4->setZOrder(1); - particle4->setScaleX(1.470f); - particle4->setScaleY(1.957f); - particle4->setID("gauntlet-particles-4"_spr); - } else if (gauntletName == "Poison") { - CCSprite* sprite = CCSprite::create("poison_gauntlet.png"_spr); - sprite->setPosition(bg->getPosition()); - sprite->setScale(1.270f/scaleFactor); - sprite->setZOrder(0); - sprite->setID("gauntlet-background"_spr); - node->addChild(sprite); - - // Particle - CCParticleSystemQuad* particle = GameToolbox::particleFromString("31a-1a3a0.3a9a90a27a11a0a37a64a0a0a0a0a0a0a50a1a0a0a0.054902a0a0.129412a0a0.0627451a0a1a0a50a1a0a0a0.054902a0a0.129412a0a0.0627451a0a1a0a0.5a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle); - particle->setPosition({0, 11}); - particle->setZOrder(1); - particle->setScaleX(1.260f); - particle->setScaleY(1.485f); - particle->setID("gauntlet-particles-1"_spr); - CCParticleSystemQuad* particle2 = GameToolbox::particleFromString("20a-1a3a0.3a6a90a22a11a0a33a55a0a0a0a0a0a0a1a1a0a0a0.137255a0a0.301961a0a0.133333a0a1a0a10a1a0a0a0.137255a0a0.301961a0a0.133333a0a1a0a0.1a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a40a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle2); - particle2->setPosition({-3, -13}); - particle2->setZOrder(1); - particle2->setScaleX(1.722f); - particle2->setScaleY(1.397f); - particle2->setID("gauntlet-particles-2"_spr); - CCParticleSystemQuad* particle3 = GameToolbox::particleFromString("4a-1a2.5a0.3a1a-90a0a0a10a35a0a0a-70a0a0a0a0a6a2a140a0a0.345098a0a0.690196a0a0.301961a0a1a0a4a2a140a0a0.345098a0a0.690196a0a0.301961a0a1a0a0a0a0a0a0a0a0a0a0a0a0a2a1a0a0a0a77a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle3); - particle3->setPosition({0, 109}); - particle3->setZOrder(1); - particle3->setScaleX(1.145f); - particle3->setScaleY(1.795f); - particle3->setID("gauntlet-particles-3"_spr); - CCParticleSystemQuad* particle4 = GameToolbox::particleFromString("40a-1a3a0.3a12a90a13a11a0a48a78a0a0a0a0a0a0a3a1a0a0a0.129412a0a0.439216a0a0.168627a0a1a0a1a1a0a0a0.129412a0a0.439216a0a0.168627a0a1a0a0.1a0a0.3a0a0a0a0a0a0a0a0a2a1a0a0a0a192a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle4); - particle4->setPosition(gauntlet->getPosition()); - particle4->setZOrder(1); - particle4->setScaleX(1.270f); - particle4->setScaleY(1.632f); - particle4->setID("gauntlet-particles-4"_spr); - } else if (gauntletName == "Shadow") { - CCSprite* sprite = CCSprite::create("shadow_gauntlet.png"_spr); - sprite->setPosition(bg->getPosition()); - sprite->setScale(1.745f/scaleFactor); - sprite->setZOrder(0); - sprite->setID("gauntlet-background"_spr); - node->addChild(sprite); - - // Show gauntlets sprite again - gauntlet->setVisible(true); - gauntlet->setPosition({2, 26}); - gauntlet->setScale(0.975f); - gauntlet_node->setZOrder(2); - - // Particle - CCParticleSystemQuad* particle = GameToolbox::particleFromString("48a-1a3a0.3a14a90a27a6a0a40a57a0a0a0a0a0a0a50a1a0a0a0.0509804a0a0.0235294a0a0.0666667a0a1a0a50a1a0a0a0.0509804a0a0.0235294a0a0.0666667a0a1a0a0.5a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle); - particle->setPosition({0, -15}); - particle->setZOrder(1); - particle->setScaleX(1.310f); - particle->setScaleY(1.735f); - particle->setID("gauntlet-particles-1"_spr); - CCParticleSystemQuad* particle2 = GameToolbox::particleFromString("62a-1a1a0.3a47a90a90a29a0a11a0a0a0a0a0a0a0a50a1a0a59a0.0784314a0a0.0235294a0a0.0941176a0a1a0a38a1a103a65a0.0784314a0a0.0235294a0a0.0941176a0a1a0a0.5a0a0.5a0a0a0a0a0a0a0a1a2a1a0a0a0a190a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle2); - particle2->setPosition({0, 25}); - particle2->setZOrder(1); - particle2->setScaleX(2.472f); - particle2->setScaleY(2.472f); - particle2->setID("gauntlet-particles-2"_spr); - CCParticleSystemQuad* particle3 = GameToolbox::particleFromString("62a-1a1a0.3a47a90a180a29a0a11a0a0a0a0a0a0a0a50a1a0a0a0.0313726a0a0.00392157a0a0.0470588a0a1a0a38a1a0a0a0.0313726a0a0.00392157a0a0.0470588a0a1a0a0.5a0a0.5a0a38a0a9a0a-85a26a1a2a1a0a0a0a183a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle3); - particle3->setPosition({0, 25}); - particle3->setZOrder(1); - particle3->setScaleX(1.470f); - particle3->setScaleY(1.470f); - particle3->setID("gauntlet-particles-3"_spr); - CCParticleSystemQuad* particle4 = GameToolbox::particleFromString("62a-1a1a0.3a47a90a180a29a0a11a0a0a0a0a0a0a0a50a1a0a0a0.0313726a0a0.00392157a0a0.0431373a0a1a0a38a1a0a0a0.0313726a0a0.00392157a0a0.0431373a0a1a0a0.5a0a0.5a0a38a0a9a0a-85a26a1a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle4); - particle4->setPosition({0, 25}); - particle4->setZOrder(1); - particle4->setScaleX(1.195f); - particle4->setScaleY(1.195f); - particle4->setID("gauntlet-particles-4"_spr); - CCParticleSystemQuad* particle5 = GameToolbox::particleFromString("40a-1a3a0.3a12a90a13a11a0a48a78a0a0a0a0a0a0a5a3a0a0a0.129412a0a0.439216a0a0.133333a0a1a0a3a3a0a0a0.129412a0a0.439216a0a0.133333a0a1a0a0.1a0a0.3a0a0a0a0a0a0a0a0a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle5); - particle5->setPosition({0, -16}); - particle5->setZOrder(1); - particle5->setScaleX(1.255f); - particle5->setScaleY(1.430f); - particle5->setID("gauntlet-particles-5"_spr); - } else if (gauntletName == "Lava") { - CCSprite* sprite = CCSprite::create("lava_gauntlet.png"_spr); - sprite->setPosition(bg->getPosition()); - sprite->setScale(1.270f/scaleFactor); - sprite->setZOrder(0); - sprite->setID("gauntlet-background"_spr); - node->addChild(sprite); - - // Show gauntlets sprite again - gauntlet->setVisible(true); - gauntlet->setPosition({0, 16}); - gauntlet->setScale(0.950f); - gauntlet_node->setZOrder(2); - - // Particle - CCParticleSystemQuad* particle = GameToolbox::particleFromString("40a-1a3a0.3a12a90a13a11a0a48a78a0a0a0a0a0a0a5a3a0a0a0.439216a0a0.309804a0a0.129412a0a1a0a3a3a0a0a0.439216a0a0.309804a0a0.129412a0a1a0a0.1a0a0.3a0a0a0a0a0a0a0a0a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle); - particle->setPosition({0, -16}); - particle->setZOrder(1); - particle->setScaleX(1.360f); - particle->setScaleY(1.360f); - particle->setID("gauntlet-particles-1"_spr); - CCParticleSystemQuad* particle2 = GameToolbox::particleFromString("59a-1a1a0.3a45a90a14a100a54a2a6a0a-135a0a46a0a0a20a8a0a0a1a0a0.756863a0a0.258824a0a1a0a1a1a0a0a1a0a0.694118a0a0.321569a0a1a0a0a0a0.1a0a0a0a0a0a0a0a0a2a1a0a0a1a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle2); - particle2->setPosition(gauntlet->getPosition()); - particle2->setZOrder(1); - particle2->setScaleX(1.522f); - particle2->setScaleY(1.547f); - particle2->setID("gauntlet-particles-2"_spr); - CCParticleSystemQuad* particle3 = GameToolbox::particleFromString("56a-1a1a0.3a43a90a16a103a81a2a6a0a-180a0a46a0a0a13a8a0a0a1a0a0.756863a0a0.258824a0a1a0a1a1a0a0a1a0a0.694118a0a0.321569a0a1a0a0a0a0.1a0a0a0a0a0a0a0a0a2a1a0a0a1a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle3); - particle3->setPosition(gauntlet->getPosition()); - particle3->setZOrder(1); - particle3->setScaleX(1.470f); - particle3->setScaleY(1.370f); - particle3->setID("gauntlet-particles-3"_spr); - CCParticleSystemQuad* particle4 = GameToolbox::particleFromString("56a-1a1a0.5a19a90a28a49a43a2a6a0a23a0a43a0a0a50a33a0a0a0.172549a0a0.160784a0a0.14902a0a0.47a0a50a38a0a0a0.247059a0a0.243137a0a0.239216a0a0.47a0a0a0a0.13a0a0a0a0a0a0a0a0a2a0a0a0a1a29a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle4); - particle4->setPosition(gauntlet->getPosition()); - particle4->setZOrder(1); - particle4->setScaleX(1.095f); - particle4->setScaleY(0.845f); - particle4->setID("gauntlet-particles-4"_spr); - CCParticleSystemQuad* particle5 = GameToolbox::particleFromString("59a-1a1a0.3a45a90a18a113a30a2a6a0a-192a0a46a0a0a20a8a0a0a1a0a0.505882a0a0.258824a0a1a0a1a1a0a0a1a0a0.505882a0a0.258824a0a1a0a0a0a0.1a0a0a0a0a0a0a0a0a2a1a0a0a1a158a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle5); - particle5->setPosition(gauntlet->getPosition()); - particle5->setZOrder(1); - particle5->setScaleX(1.255f); - particle5->setScaleY(1.430f); - particle5->setID("gauntlet-particles-5"_spr); - CCParticleSystemQuad* particle6 = GameToolbox::particleFromString("48a-1a3a0.3a14a90a27a6a0a40a57a0a0a0a0a0a0a50a1a0a0a0.0666667a0a0.0352941a0a0.0196078a0a1a0a50a1a0a0a0.0666667a0a0.0352941a0a0.0196078a0a1a0a0.5a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle6); - particle6->setPosition({0, -18}); - particle6->setZOrder(1); - particle6->setScaleX(1.235f); - particle6->setScaleY(1.835f); - particle6->setID("gauntlet-particles-6"_spr); - } else if (gauntletName == "Time") { - CCSprite* sprite = CCSprite::create("time_gauntlet.png"_spr); - sprite->setPosition(bg->getPosition()); - sprite->setScale(1.220f/scaleFactor); - sprite->setZOrder(0); - sprite->setID("gauntlet-background"_spr); - node->addChild(sprite); - } else if (gauntletName == "Spike") { - CCSprite* sprite = CCSprite::create("spike_gauntlet.png"_spr); - sprite->setPosition(bg->getPosition()); - sprite->setScale(1.920f/scaleFactor); - sprite->setZOrder(0); - sprite->setID("gauntlet-background"_spr); - node->addChild(sprite); - } else if (gauntletName == "Cyborg") { - CCSprite* sprite = CCSprite::create("cyborg_gauntlet.png"_spr); - sprite->setPosition(bg->getPosition()); - sprite->setScale(1.270f/scaleFactor); - sprite->setZOrder(0); - sprite->setID("gauntlet-background"_spr); - node->addChild(sprite); - } else if (gauntletName == "Inferno") { - CCSprite* sprite = CCSprite::create("inferno_gauntlet.png"_spr); - sprite->setPosition(bg->getPosition()); - sprite->setScale(1.270f/scaleFactor); - sprite->setZOrder(0); - sprite->setID("gauntlet-background"_spr); - node->addChild(sprite); - } else if (gauntletName == "Death") { - CCSprite* sprite = CCSprite::create("death_gauntlet.png"_spr); - sprite->setPosition(bg->getPosition()); - sprite->setScale(1.270f/scaleFactor); - sprite->setZOrder(0); - sprite->setID("gauntlet-background"_spr); - node->addChild(sprite); - } else if (gauntletName == "Magic") { - CCSprite* sprite = CCSprite::create("magic_gauntlet.png"_spr); - sprite->setPosition(bg->getPosition()); - sprite->setScale(1.270f/scaleFactor); - sprite->setZOrder(0); - sprite->setID("gauntlet-background"_spr); - node->addChild(sprite); - } else if (gauntletName == "Chaos") { - CCSprite* sprite = CCSprite::create("chaos_gauntlet.png"_spr); - sprite->setPosition(bg->getPosition()); - sprite->setScale(1.270f/scaleFactor); - sprite->setZOrder(0); - sprite->setID("gauntlet-background"_spr); - node->addChild(sprite); - } else if (gauntletName == "Force") { - CCSprite* sprite = CCSprite::create("force_gauntlet.png"_spr); - sprite->setPosition(bg->getPosition()); - sprite->setScale(1.270f/scaleFactor); - sprite->setZOrder(0); - sprite->setID("gauntlet-background"_spr); - node->addChild(sprite); - } else if (gauntletName == "NCS I") { - CCSprite* sprite = CCSprite::create("ncs_gauntlet_1.png"_spr); - sprite->setPosition(bg->getPosition()); - sprite->setScale(1.270f/scaleFactor); - sprite->setZOrder(0); - sprite->setID("gauntlet-background"_spr); - node->addChild(sprite); - - // Show gauntlets sprite again - gauntlet->setVisible(true); - gauntlet->setPosition({0, 16}); - gauntlet->setScale(0.9f); - gauntlet_node->setZOrder(3); - - CCParticleSystemQuad* particle = GameToolbox::particleFromString("25a-1a1a0.3a19a90a90a0a0a0a0a0a0a0a0a0a0a23a1a0a98a0.0980392a0a0.176471a0a0.219608a0a1a0a50a1a0a0a0.0980392a0a0.176471a0a0.219608a0a1a0a0a0a0a0a0a0a0a0a0a0a0a2a1a0a0a0a185a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle); - particle->setPosition({0, 20}); - particle->setZOrder(2); - particle->setScale(2.060f); - particle->setID("gauntlet-particles-1"_spr); - - CCParticleSystemQuad* particle2 = GameToolbox::particleFromString("25a-1a1a0.3a19a90a90a0a0a0a0a0a0a0a0a0a0a12a1a0a98a0.0980392a0a0.176471a0a0.219608a0a1a0a36a1a0a0a0.0980392a0a0.176471a0a0.219608a0a1a0a0a0a0a0a0a0a0a0a0a0a0a2a1a0a0a0a167a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle2); - particle2->setPosition(gauntlet->getPosition()); - particle2->setZOrder(2); - particle2->setScale(4.210f); - particle2->setID("gauntlet-particles-2"_spr); - - CCParticleSystemQuad* particle3 = GameToolbox::particleFromString("30a-1a2.5a0.3a10a90a12a19a0a23a0a0a11a0a0a0a0a4a1a0a0a0.313726a0a0.458824a0a0.529412a0a1a0a2a1a0a0a0.313726a0a0.458824a0a0.529412a0a1a0a0a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a19a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle3); - particle3->setZOrder(0); - particle3->setPosition({0, -81}); - particle3->setScale(2.035f); - particle3->setID("gauntlet-particles-3"_spr); - } else if (gauntletName == "NCS II") { - CCSprite* sprite = CCSprite::create("ncs_gauntlet_2.png"_spr); - sprite->setPosition(bg->getPosition()); - sprite->setScale(1.270f/scaleFactor); - sprite->setZOrder(0); - sprite->setID("gauntlet-background"_spr); - node->addChild(sprite); - - // Show gauntlets sprite again - gauntlet->setVisible(true); - gauntlet->setPosition({0, 16}); - gauntlet->setScale(0.9f); - gauntlet_node->setZOrder(3); - - CCParticleSystemQuad* particle = GameToolbox::particleFromString("25a-1a1a0.3a19a90a90a0a0a0a0a0a0a0a0a0a0a23a1a0a98a0.301961a0a0.156863a0a0.341176a0a1a0a50a1a0a0a0.301961a0a0.156863a0a0.341176a0a1a0a0a0a0a0a0a0a0a0a0a0a0a2a1a0a0a0a185a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle); - particle->setPosition({0, 20}); - particle->setZOrder(2); - particle->setScale(2.060f); - particle->setID("gauntlet-particles-1"_spr); - - CCParticleSystemQuad* particle2 = GameToolbox::particleFromString("25a-1a1a0.3a19a90a90a0a0a0a0a0a0a0a0a0a0a12a1a0a98a0.301961a0a0.156863a0a0.341176a0a1a0a36a1a0a0a0.301961a0a0.156863a0a0.341176a0a1a0a0a0a0a0a0a0a0a0a0a0a0a2a1a0a0a0a167a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle2); - particle2->setPosition(gauntlet->getPosition()); - particle2->setZOrder(2); - particle2->setScale(4.210f); - particle2->setID("gauntlet-particles-2"_spr); - - CCParticleSystemQuad* particle3 = GameToolbox::particleFromString("30a-1a2.5a0.3a10a90a12a19a0a23a0a0a11a0a0a0a0a4a1a0a0a0.482353a0a0.317647a0a0.533333a0a1a0a2a1a0a0a0.482353a0a0.317647a0a0.533333a0a1a0a0a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a19a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle3); - particle3->setZOrder(0); - particle3->setPosition({0, -81}); - particle3->setScale(2.035f); - particle3->setID("gauntlet-particles-3"_spr); - } else if (gauntletName == "Discord") { - CCSprite* sprite = CCSprite::create("discord_gauntlet.png"_spr); - sprite->setPosition({bg->getPositionX(), bg->getPositionY() + 3}); - sprite->setScale(1.245f/scaleFactor); - sprite->setZOrder(0); - item->setZOrder(1); - sprite->setID("gauntlet-background"_spr); - node->addChild(sprite); - } else if (gauntletName == "Space") { - CCSprite* sprite = CCSprite::create("space_gauntlet.png"_spr); - sprite->setPosition({bg->getPositionX(), bg->getPositionY() + 3}); - sprite->setScale(1.245f/scaleFactor); - sprite->setZOrder(0); - item->setZOrder(1); - sprite->setID("gauntlet-background"_spr); - node->addChild(sprite); - } else if (gauntletName == "Cosmos") { - CCSprite* sprite = CCSprite::create("cosmos_gauntlet.png"_spr); - sprite->setPosition({bg->getPositionX(), bg->getPositionY() + 3}); - sprite->setScale(1.245f/scaleFactor); - sprite->setZOrder(0); - item->setZOrder(1); - sprite->setID("gauntlet-background"_spr); - node->addChild(sprite); - } else { - bg->setVisible(true); - label->setVisible(true); - label_shadow->setVisible(true); - label_gauntlet->setVisible(true); - label_shadow_gauntlet->setVisible(true); - gauntlet->setVisible(true); - gauntlet_shadow->setVisible(true); - } - } - } - } -}; diff --git a/src/Styles/IconLockColors.cpp b/src/Styles/IconLockColors.cpp deleted file mode 100644 index a930cc4..0000000 --- a/src/Styles/IconLockColors.cpp +++ /dev/null @@ -1,134 +0,0 @@ -#include "../includes.h" -#include -#include -#ifndef GEODE_IS_IOS -class IconLockColors { - public: - void onP1(CCObject* pSender) { - SimplePlayer* player = this->getPlayer(CCDirector::sharedDirector()->getRunningScene()); - - GameManager* gm = GameManager::sharedState(); - player->setColor(gm->colorForIdx(gm->getPlayerColor())); - player->setSecondColor(gm->colorForIdx(gm->getPlayerColor2())); - }; - - void onP2(CCObject* pSender) { - SimplePlayer* player = this->getPlayer(CCDirector::sharedDirector()->getRunningScene()); - - GameManager* gm = GameManager::sharedState(); - player->setColor(gm->colorForIdx(gm->getPlayerColor2())); - player->setSecondColor(gm->colorForIdx(gm->getPlayerColor())); - }; - - void onGlow(CCObject* pSender) { - SimplePlayer* player = this->getPlayer(CCDirector::sharedDirector()->getRunningScene()); - GameManager* gm = GameManager::sharedState(); - - if (!player->m_hasGlowOutline) { - player->setGlowOutline(gm->colorForIdx(gm->getPlayerGlowColor())); - } else { - player->disableGlowOutline(); - } - }; - - static SimplePlayer* getPlayer(CCScene* scene) { - CCArray* children = scene->getChildren(); - - for (int i = 0; i < children->count(); i++) { - ItemInfoPopup* child = typeinfo_cast(children->objectAtIndex(i)); - - if (child) { - CCArray* array_childrens = typeinfo_cast(child->getChildren()->objectAtIndex(0))->getChildren(); - - for (int j = 0; j < array_childrens->count(); j++) { - GJItemIcon* node = typeinfo_cast(array_childrens->objectAtIndex(j)); - - if (node) { - SimplePlayer* player = typeinfo_cast(node->getChildren()->objectAtIndex(0)); - return player; - } - } - } - } - - return nullptr; - } - - static void createIconColor() { - auto scene = CCDirector::sharedDirector()->getRunningScene(); - CCArray* children = scene->getChildren(); - - for (int i = 0; i < children->count(); i++) { - ItemInfoPopup* child = typeinfo_cast(children->objectAtIndex(i)); - - if (child) { - CCArray* array_childrens = typeinfo_cast(child->getChildren()->objectAtIndex(0))->getChildren(); - - SimplePlayer* player = getPlayer(CCDirector::sharedDirector()->getRunningScene()); - GameManager* gm = GameManager::sharedState(); - if (player) { - player->setColor(gm->colorForIdx(gm->getPlayerColor())); - player->setSecondColor(gm->colorForIdx(gm->getPlayerColor2())); - player->m_glowColor = gm->colorForIdx(gm->getPlayerGlowColor()); - - // Creating buttons - auto p1Spr = CCSprite::createWithSpriteFrameName("GJ_checkOff_001.png"); - auto p1txt = CCLabelBMFont::create("P1", "bigFont.fnt"); - p1txt->setScale(.525f); - p1txt->setPosition(15, 17); - p1Spr->addChild(p1txt); - p1Spr->setScale(.8f); - auto p1Btn = CCMenuItemSpriteExtra::create( - p1Spr, - child, - menu_selector(IconLockColors::onP1) - ); - p1Btn->setPosition(-128, -40); - p1Btn->setID("p1-button"_spr); - child->m_buttonMenu->addChild(p1Btn); - - auto p2Spr = CCSprite::createWithSpriteFrameName("GJ_checkOff_001.png"); - auto p2txt = CCLabelBMFont::create("P2", "bigFont.fnt"); - p2txt->setScale(.525f); - p2txt->setPosition(15, 17); - p2Spr->addChild(p2txt); - p2Spr->setScale(.8f); - auto p2Btn = CCMenuItemSpriteExtra::create( - p2Spr, - child, - menu_selector(IconLockColors::onP2) - ); - p2Btn->setPosition(-99, -40); - p2Btn->setID("p2-button"_spr); - child->m_buttonMenu->addChild(p2Btn); - - auto glowSpr = CCSprite::createWithSpriteFrameName("GJ_checkOff_001.png"); - auto glowtxt = CCLabelBMFont::create("G", "bigFont.fnt"); - glowtxt->setScale(.525f); - glowtxt->setPosition(15, 17); - glowSpr->addChild(glowtxt); - glowSpr->setScale(.8f); - auto glowBtn = CCMenuItemSpriteExtra::create( - glowSpr, - child, - menu_selector(IconLockColors::onGlow) - ); - glowBtn->setPosition(-69, -40); - glowBtn->setID("glow-button"_spr); - child->m_buttonMenu->addChild(glowBtn); - } - } - } - } -}; - -class $modify(GJGarageLayer) { - void onSelect(cocos2d::CCObject* sender) { - GJGarageLayer::onSelect(sender); - - if (!Mod::get()->template getSettingValue("previewIcons")) return; - - IconLockColors::createIconColor(); - } -}; -#endif diff --git a/src/Styles/NewGJCommentList.cpp b/src/Styles/NewGJCommentList.cpp deleted file mode 100644 index cac42c8..0000000 --- a/src/Styles/NewGJCommentList.cpp +++ /dev/null @@ -1,216 +0,0 @@ -#include "NewGJCommentList.h" -#include "Geode/loader/Log.hpp" -#include "NewProfilePage.h" -#include "../MoreLeaderboards/MoreLeaderboards.h" -#include "ccTypes.h" -#include -#include -#include - -std::string badgesDataComments = ""; - -int hexStringToInt(const std::string& hexStr) { - int value; - std::istringstream iss(hexStr); - iss >> std::hex >> value; - return value; -} - -// Mod badges descriptions & GDUtils dev badge -class $modify(CommentCell) { - struct Fields { - EventListener m_listener; - }; - - void badgeHandle(std::string result, CCLayer* layer, int commentID) { - if (this->m_accountComment) return; - - CommentCell* cell = this; - int accountID = cell->m_comment->m_accountID; - - // Part for handling GDUtils badges - if (!Mod::get()->template getSettingValue("customBadges")) return; - - if (result.empty()) return; - - std::string data = result; - std::vector data_user = MoreLeaderboards::getWords(data, "|"); - - CCMenu* username_menu = typeinfo_cast(cell->getChildByIDRecursive("username-menu")); - - bool colorSet = false; - - while (data_user.size() > 0) { - std::vector data = MoreLeaderboards::getWords(data_user[0], "?"); - - int badge = 0; - int accountID_data = 0; - std::string badge_id = ""; - std::string badge_sprite = ""; - float badge_scale = 1.0f; - std::string alertTitle = ""; - std::string alertDesc = ""; - float alertWidth = 300.0f; - bool isKofi = false; - ccColor3B color = { 255, 255, 255 }; - - while (data.size() > 0) { - std::string id = data[0]; - std::string name = data[1]; - - if (id == "1") { - accountID_data = geode::utils::numFromString(name).unwrapOrDefault(); - } else if (id == "3") { - badge = geode::utils::numFromString(name).unwrapOrDefault(); - } else if (id == "4") { - badge_sprite = name; - } else if (id == "5") { - badge_id = name; - } else if (id == "7") { - badge_scale = geode::utils::numFromString(name).unwrapOrDefault(); - } else if (id == "8") { - alertTitle = name; - } else if (id == "9") { - alertDesc = name; - } else if (id == "10") { - alertWidth = geode::utils::numFromString(name).unwrapOrDefault(); - } else if (id == "11") { - isKofi = name == "1"; - } else if (id == "12") { - // Get color from HEX - color = ccc3( - hexStringToInt(name.substr(0, 2)), - hexStringToInt(name.substr(2, 2)), - hexStringToInt(name.substr(4, 2)) - ); - } - - data.erase(data.begin()); - data.erase(data.begin()); - } - - if (accountID_data == accountID) { - if (!cell->getChildByIDRecursive(badge_id)) { - auto badgeGDUtil = CCSprite::createWithSpriteFrameName(fmt::format("{}"_spr, badge_sprite).c_str()); - badgeGDUtil->setScale(badge_scale); - auto badgeGDUtilBtn = CCMenuItemSpriteExtra::create( - badgeGDUtil, - cell, - menu_selector(NewProfilePage::badgeFactoryAlert) - ); - - if (auto commentText = cell->getChildByIDRecursive("comment-text-label")) { - if (color.r != 255 && color.g != 255 && color.b != 255 && !colorSet) { - as(commentText)->setColor(color); - cell->m_comment->m_color = color; - colorSet = true; - } - } - if (auto commentText = cell->getChildByIDRecursive("comment-text-area")) { - TextArea* textArea = as(commentText); - MultilineBitmapFont* bmFont = as(textArea->getChildren()->objectAtIndex(0)); - CCArray* children = bmFont->getChildren(); - for (int i = 0; i < children->count(); i++) { - auto child = as(children->objectAtIndex(i)); - if (color.r != 255 && color.g != 255 && color.b != 255 && !colorSet) { - child->setColor(color); - colorSet = true; - } - } - } - - badgeGDUtilBtn->setID(badge_id); - - auto array = CCArray::create(); - array->addObject(CCString::create(alertTitle)); - array->addObject(CCString::create(alertDesc)); - array->addObject(CCFloat::create(alertWidth)); - array->addObject(CCString::create(isKofi ? "1" : "0")); - badgeGDUtilBtn->setUserObject(array); - - if (cell->getChildByIDRecursive("percentage-label")) { - username_menu->insertBefore(badgeGDUtilBtn, cell->getChildByIDRecursive("percentage-label")); - } else { - username_menu->addChild(badgeGDUtilBtn); - } - username_menu->updateLayout(); - } - } - - data_user.erase(data_user.begin()); - } - } - - void loadFromComment(GJComment* comment) { - CommentCell::loadFromComment(comment); - - if (comment->m_commentDeleted) return; - - CCMenuItemSpriteExtra* badgeBtn = nullptr; - CommentCell* cell = this; - CCSprite* modbadge = typeinfo_cast(cell->getChildByIDRecursive("mod-badge")); - std::string username = cell->m_comment->m_userName; - CCMenu* username_menu = typeinfo_cast(cell->getChildByIDRecursive("username-menu")); - - if (modbadge != nullptr) { - modbadge->removeFromParentAndCleanup(true); - - if (username == "RobTop" && Mod::get()->template getSettingValue("customBadges")) { - auto robtopSpr = CCSprite::createWithSpriteFrameName("robtop_badge.png"_spr); - robtopSpr->setScale(0.75f); - badgeBtn = CCMenuItemSpriteExtra::create( - robtopSpr, - this, - menu_selector(NewProfilePage::onRobTopBadgePressed) - ); - } else { - CCSprite* modSprite; - if (comment->m_modBadge == 1) { - modSprite = CCSprite::createWithSpriteFrameName("modBadge_01_001.png"); - } else if (comment->m_modBadge == 2) { - modSprite = CCSprite::createWithSpriteFrameName("modBadge_02_001.png"); - } else if (comment->m_modBadge == 3) { - modSprite = CCSprite::createWithSpriteFrameName("modBadge_03_001.png"); - } - modSprite->setScale(0.6f); - - badgeBtn = CCMenuItemSpriteExtra::create( - modSprite, - this, - menu_selector(NewProfilePage::onBadgePressed) - ); - } - - badgeBtn->setID("mod-badge"); - badgeBtn->setUserObject(CCInteger::create(comment->m_modBadge)); - if (cell->getChildByIDRecursive("percentage-label")) { - username_menu->insertBefore(badgeBtn, cell->getChildByIDRecursive("percentage-label")); - } else { - username_menu->addChild(badgeBtn); - } - } - - if (username_menu != nullptr) username_menu->updateLayout(); - - const std::function then = [this, cell](std::string const& result) { - badgeHandle(result, cell, cell->m_comment->m_commentID); - }; - - if (!badgesDataComments.empty()) { - then(badgesDataComments); - return; - } - - m_fields->m_listener.bind([then = std::move(then)] (web::WebTask::Event* e) { - if (web::WebResponse* res = e->getValue()) { - if (res->ok()) { - badgesDataComments = res->string().unwrapOrDefault(); - then(badgesDataComments); - } - } - }); - - auto req = web::WebRequest(); - m_fields->m_listener.setFilter(req.get("https://clarifygdps.com/gdutils/gdutils_roles.php?version=1.0")); - } -}; diff --git a/src/Styles/NewGJCommentList.h b/src/Styles/NewGJCommentList.h deleted file mode 100644 index 422186b..0000000 --- a/src/Styles/NewGJCommentList.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once -#include "../includes.h" -#include - -using namespace geode::prelude; \ No newline at end of file diff --git a/src/Styles/NewProfilePage.cpp b/src/Styles/NewProfilePage.cpp deleted file mode 100644 index c3d9087..0000000 --- a/src/Styles/NewProfilePage.cpp +++ /dev/null @@ -1,344 +0,0 @@ -#include "NewProfilePage.h" -#include "../includes.h" -#include "Geode/utils/general.hpp" -#include -#include - -std::string badgesDataProfile = ""; - -void NewProfilePage::onBadgePressed(CCObject* pSender) { - geode::log::info("Object: {}", static_cast(pSender)->getUserObject()); - CCInteger* score = static_cast(static_cast(pSender)->getUserObject()); - geode::log::info("Badge pressed: {}", score->getValue()); - switch (score->getValue()) { - case 1: - #ifndef GEODE_IS_ANDROID - FLAlertLayer::create( - nullptr, - "Geometry Dash Moderator", - R"text( - A Geometry Dash Moderator is a user that has been chosen by RobTop and other Moderators on the team by suggestions. - - Their first task is to suggest levels to be rated to RobTop, the current developer of the game and the maintainer of the level rating system, he is the only one that can rate levels. - - How to become a Geometry Dash Moderator? - - Many community members want to become a Moderator to be able to suggest levels for a rating. Similar to rating, Moderator promotions are solely done by RobTop himself on a case-by-case basis, so there are no clear steps to getting it. Here are some general criteria: - - Be nice. - - Be active in the community. - - Be trustworthy. - - Be able to judge levels well. - - If you display these qualities over time, there is a chance that other Moderators may suggest you to RobTop. Of course, showing that you are qualified may be difficult if you do not share circles with pre-existing Moderators. If you are interested, you can apply to join the team using our application form. Make sure you read the information in it clearly before submitting. Here is the link: - - https://forms.gle/XyRGtFzD3hshDUQV7 - )text", - "OK", - nullptr, - 400.0f, - true, - 100.0f, - 1.0f - )->show(); - #else - FLAlertLayer::create( - nullptr, - "Geometry Dash Moderator", - R"text( - A Geometry Dash Moderator is a user that has been chosen by RobTop and other Moderators on the team by suggestions. - - Their first task is to suggest levels to be rated to RobTop, the current developer of the game and the maintainer of the level rating system, he is the only one that can rate levels. - )text", - "OK", - nullptr, - 400.0f, - true, - 100.0f, - 1.0f - )->show(); - - #endif - break; - case 2: - #ifndef GEODE_IS_ANDROID // android height broken - FLAlertLayer::create( - nullptr, - "Geometry Dash Elder Moderator", - R"text( - A Geometry Dash Elder Moderator is a user that has been chosen by RobTop and other Moderators on the team by suggestions and was already a Geometry Dash Moderator before his promotion. They are the pillars of the moderation team. - - Their first task is to suggest levels to be rated to RobTop, the current developer of the game and the maintainer of the level rating system, which he is the only one that can rate levels. But they also have the power to moderate comments, whitelists Newgrounds artists and unlocks updates for old rated levels. - - How to become a Geometry Dash Moderator? - - Many community members want to become a Moderator to be able to suggest levels for a rating. Similar to rating, Moderator promotions are solely done by RobTop himself on a case-by-case basis, so there are no clear steps to getting it. Here are some general criteria: - - Be nice. - - Be active in the community. - - Be trustworthy. - - Be able to judge levels well. - - If you display these qualities over time, there is a chance that other Moderators may suggest you to RobTop. Of course, showing that you are qualified may be difficult if you do not share circles with pre-existing Moderators. If you are interested, you can apply to join the team using our application form. Make sure you read the information in it clearly before submitting. Here is the link: - - https://forms.gle/XyRGtFzD3hshDUQV7 - )text", - "OK", - nullptr, - 400.0f, - true, - 100.0f, - 1.0f - )->show(); - #else - FLAlertLayer::create( - nullptr, - "Geometry Dash Elder Moderator", - R"text( - A Geometry Dash Elder Moderator is a user that has been chosen by RobTop and other Moderators on the team by suggestions and was already a Geometry Dash Moderator before his promotion. They are the pillars of the moderation team. - - Their first task is to suggest levels to be rated to RobTop, the current developer of the game and the maintainer of the level rating system, which he is the only one that can rate levels. But they also have the power to moderate comments, whitelists Newgrounds artists and unlocks updates for old rated levels. - )text", - "OK", - nullptr, - 400.0f, - true, - 100.0f, - 1.0f - )->show(); - #endif - break; - case 3: - FLAlertLayer::create( - nullptr, - "Geometry Dash Leaderboard Moderator", - R"text( -A Geometry Dash Leaderboard Moderator is a user that has been chosen by RobTop and other Leaderboard Moderators on the team by suggestions. - -Their first task is to moderate the Top 100 / Global leaderboard (as well as level-specific ones) and ban cheaters to keep the leaderboards clean. - -How to become a Geometry Dash Leaderboard Moderator? - -If you are interested, you can apply to join the team using our application form. Make sure you read the information in it clearly before submitting. Here is the link: - -https://docs.google.com/forms/d/e/1FAIpQLSfrJyIVM7KzPyTgzGZ0CSuGBPv40160W9I6bPiMgLRawjJJBg/viewform - )text", - "OK", - nullptr, - 500.0f, - true, - 40.0f, - 1.0f - )->show(); - break; - } -} - -void NewProfilePage::onRobTopBadgePressed(CCObject* pSender) { - FLAlertLayer::create( - nullptr, - "RobTop Games", - R"text( -RobTop is the creator and the main developer of Geometry Dash. - -He is the only developer of the game and is responsible for all the updates and content that is added to the game. - )text", - "OK", - nullptr, - 300.0f - )->show(); -} - -void NewProfilePage::badgeFactoryAlert(CCObject* pSender) { - CCArray* children = static_cast(static_cast(pSender)->getUserObject()); - - std::string alertTitle = static_cast(children->objectAtIndex(0))->getCString(); - std::string alertDesc = static_cast(children->objectAtIndex(1))->getCString(); - float alertWidth = static_cast(children->objectAtIndex(2))->getValue(); - bool isKofi = strcmp(static_cast(children->objectAtIndex(3))->getCString(), "1") == 0; - - // Fix \n in alertDesc - std::string::size_type n = 0; - while ((n = alertDesc.find("\\n", n)) != std::string::npos) { - alertDesc.replace(n, 2, "\n"); - n += 1; - } - - if (isKofi) { - geode::createQuickPopup( - alertTitle.c_str(), - alertDesc.c_str(), - "OK", "Ko-Fi", - [](auto, bool btn2) { - if (btn2) { - geode::utils::web::openLinkInBrowser("https://ko-fi.com/gdutils"); - } - }, - alertWidth - ); - } else { - FLAlertLayer::create( - nullptr, - alertTitle.c_str(), - alertDesc.c_str(), - "OK", - nullptr, - alertWidth - )->show(); - } -} - -// Mod badges descriptions & GDUtils dev badge -class $modify(ProfilePage) { - struct Fields { - EventListener m_listener; - }; - - void requestGDUtilsBadges(int accountID, CCLayer* layer) { - const std::function then = [this, accountID, layer](std::string const& result) { - badgesDataProfile = result; - - std::vector data_user = MoreLeaderboards::getWords(result, "|"); - - while (data_user.size() > 0) { - std::vector data = MoreLeaderboards::getWords(data_user[0], "?"); - - int badge = 0; - int accountID_data = 0; - std::string badge_id = ""; - std::string badge_sprite = ""; - float badge_scale = 1.0f; - std::string alertTitle = ""; - std::string alertDesc = ""; - float alertWidth = 300.0f; - bool isKofi = false; - - while (data.size() > 0) { - std::string id = data[0]; - std::string name = data[1]; - - if (id == "1") { - accountID_data = numFromString(name).unwrapOrDefault(); - } else if (id == "3") { - badge = numFromString(name).unwrapOrDefault(); - } else if (id == "4") { - badge_sprite = name; - } else if (id == "5") { - badge_id = name; - } else if (id == "6") { - badge_scale = numFromString(name).unwrapOrDefault(); - } else if (id == "8") { - alertTitle = name; - } else if (id == "9") { - alertDesc = name; - } else if (id == "10") { - alertWidth = numFromString(name).unwrapOrDefault(); - } else if (id == "11") { - isKofi = name == "1"; - } - - data.erase(data.begin()); - data.erase(data.begin()); - } - - if (accountID_data == accountID) { - if (!layer->getChildByIDRecursive(badge_id)) { - CCMenu* username_menu = typeinfo_cast(layer->getChildByIDRecursive("username-menu")); - - auto badgeGDUtil = CCSprite::createWithSpriteFrameName(fmt::format("{}"_spr, badge_sprite).c_str()); - badgeGDUtil->setScale(badge_scale); - - auto badgeGDUtilBtn = CCMenuItemSpriteExtra::create( - badgeGDUtil, - layer, - menu_selector(NewProfilePage::badgeFactoryAlert) - ); - - badgeGDUtilBtn->setID(badge_id); - - auto array = CCArray::create(); - array->addObject(CCString::create(alertTitle)); - array->addObject(CCString::create(alertDesc)); - array->addObject(CCFloat::create(alertWidth)); - array->addObject(CCString::create(isKofi ? "1" : "0")); - badgeGDUtilBtn->setUserObject(array); - - username_menu->addChild(badgeGDUtilBtn); - username_menu->updateLayout(); - } - } - - data_user.erase(data_user.begin()); - } - }; - const std::function expect = [this](std::string const& error) { - log::error("Failed to get GDUtils badges: {}", error); - }; - - // If we already have the badges data, don't request it again - if (badgesDataProfile != "") { - then(badgesDataProfile); - return; - } - - // Request the badges data - m_fields->m_listener.bind([expect = std::move(expect), then = std::move(then)] (web::WebTask::Event* e) { - if (web::WebResponse* res = e->getValue()) { - if (res->ok()) { - then(res->string().unwrapOrDefault()); - } else { - expect("Request failed"); - } - } - }); - - auto req = web::WebRequest(); - m_fields->m_listener.setFilter(req.get("https://clarifygdps.com/gdutils/gdutils_roles.php?version=1.0")); - } - - void loadPageFromUserInfo(GJUserScore* a2) { - ProfilePage::loadPageFromUserInfo(a2); - auto layer = m_mainLayer; - CCMenuItemSpriteExtra* badgeBtn = nullptr; - - CCMenu* username_menu = typeinfo_cast(layer->getChildByIDRecursive("username-menu")); - CCLabelBMFont* label = typeinfo_cast(layer->getChildByIDRecursive("username-label")); - - if (layer) { - CCSprite* modbadge = typeinfo_cast(layer->getChildByIDRecursive("mod-badge")); - if (modbadge != nullptr) { - modbadge->removeFromParentAndCleanup(true); - - if (a2->m_userName == "RobTop" && Mod::get()->template getSettingValue("customBadges")) { - auto robtopSpr = CCSprite::createWithSpriteFrameName("robtop_badge.png"_spr); - badgeBtn = CCMenuItemSpriteExtra::create( - robtopSpr, - this, - menu_selector(NewProfilePage::onRobTopBadgePressed) - ); - } else { - CCSprite* modSprite; - if (a2->m_modBadge == 1) { - modSprite = CCSprite::createWithSpriteFrameName("modBadge_01_001.png"); - } else if (a2->m_modBadge == 2) { - modSprite = CCSprite::createWithSpriteFrameName("modBadge_02_001.png"); - } else if (a2->m_modBadge == 3) { - modSprite = CCSprite::createWithSpriteFrameName("modBadge_03_001.png"); - } - - badgeBtn = CCMenuItemSpriteExtra::create( - modSprite, - this, - menu_selector(NewProfilePage::onBadgePressed) - ); - } - - badgeBtn->setID("mod-badge"); - badgeBtn->setUserObject(CCInteger::create(a2->m_modBadge)); - username_menu->addChild(badgeBtn); - } - - if (username_menu != nullptr) username_menu->updateLayout(); - } - - if (Mod::get()->template getSettingValue("customBadges")) requestGDUtilsBadges(a2->m_accountID, layer); - } -}; diff --git a/src/Styles/NewProfilePage.h b/src/Styles/NewProfilePage.h deleted file mode 100644 index 0b43953..0000000 --- a/src/Styles/NewProfilePage.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once -#include -#include -#include "../MoreLeaderboards/MoreLeaderboards.h" - -using namespace geode::prelude; - -class NewProfilePage : public CCLayer { - public: - void onBadgePressed(CCObject* pSender); - void onRobTopBadgePressed(CCObject* pSender); - void badgeFactoryAlert(CCObject* pSender); -}; diff --git a/src/Styles/TheVault.cpp b/src/Styles/TheVault.cpp deleted file mode 100644 index 2619a8b..0000000 --- a/src/Styles/TheVault.cpp +++ /dev/null @@ -1,27 +0,0 @@ -#include "../includes.h" -#include - -class $modify(SecretLayer) { - bool init() { - if (!SecretLayer::init()) return false; - - if (!Mod::get()->template getSettingValue("secretLayerDesign")) return true; - - auto winSize = CCDirector::sharedDirector()->getWinSize(); - auto bg = CCSprite::createWithSpriteFrameName("thevault.png"_spr); - - bg->setPosition(winSize / 2); - bg->setZOrder(-1); - bg->setScaleY(winSize.height / bg->getContentSize().height); - bg->setScaleX(winSize.width / bg->getContentSize().width); - this->addChild(bg); - - // Get vault name - auto vaultName = this->getChildByID("vault-name"); - if (vaultName) { - vaultName->setVisible(false); - } - - return true; - } -}; diff --git a/src/Utils/DemonList.cpp b/src/Utils/DemonList.cpp deleted file mode 100644 index 97c8e1a..0000000 --- a/src/Utils/DemonList.cpp +++ /dev/null @@ -1,205 +0,0 @@ -#include "../includes.h" -#include -#include -#include -#include "../Settings/CustomSettings.hpp" - - -// demon list -std::unordered_map demonListCache; // Will clear after game exit, or if user deletes level -static std::unordered_map RUNNING_REQUESTS {}; -static std::mutex lock_var; - -// love url encoded characters :D -// also for some reason this is required on mac because Geode's web requests doesnt automatically append this for some reason. -// SURELY THIS WONT AFFECT LEVELS WITH SOME OTHER BREAKING SYMBOLS. -std::string url_encode(const std::string& value) { - std::string encoded; - encoded.reserve(value.size()); - for (char c : value) { - if (c == ' ') { - encoded += "%20"; - } else { - encoded += c; - } - } - return encoded; -} - -class $modify(LevelInfoLayer) { - // chat jippity - void set(int key, int value) { - demonListCache[key] = value; - } - - // Function to check if a key exists in the cache. - bool exists(int key) { - return demonListCache.find(key) != demonListCache.end(); - } - - // Function to get a value from the cache. - int get(int key) { - auto it = demonListCache.find(key); - if (it != demonListCache.end()) { - return it->second; - } - // Return -1 or some other default value to indicate the key wasn't found. - return -1; - } - - bool remove(int key) { - return demonListCache.erase(key) == 1; - } - float getScaleBasedPos(int pos) { - if (pos > 0 && pos < 10) return 0.5F; - if (pos > 10 && pos < 100) return 0.4F; - if (pos > 100 && pos < 1000) return 0.3F; - if (pos > 1000 && pos < 10000) return 0.2F; - return 0.1F; - } - bool init(GJGameLevel* level, bool p1) { // inspiration le gdbrowser, what does p1 do? idk - if (!LevelInfoLayer::init(level, p1)) return false; - if (!Mod::get()->template getSettingValue("demonListPlacement")) return true; - - if (level->m_demon.value() == 0 || level->m_stars.value() != 10) return true; - if (level->m_demonDifficulty != 6) return true; - - auto director = CCDirector::sharedDirector(); - auto winSize = director->getWinSize(); - auto demonSpr = CCSprite::createWithSpriteFrameName("diffIcon_06_btn_001.png"); - demonSpr->setPosition({(winSize.width / 2) - 85, (winSize.height / 2) + 18}); - demonSpr->setScale(0.5F); - demonSpr->setID("demon-spr"_spr); - auto positionLabel = CCLabelBMFont::create("N/A", "bigFont.fnt"); - positionLabel->setPosition({(winSize.width / 2) - 107, (winSize.height / 2) + 18}); // 178 - positionLabel->setScale(0.5F); - positionLabel->setVisible(false); - positionLabel->setID("demon-pos"_spr); - demonSpr->setVisible(false); - this->addChild(demonSpr); - this->addChild(positionLabel); - if (level->m_coins > 0) { - positionLabel->setPositionY(positionLabel->getPositionY() - 18); - demonSpr->setPositionY(demonSpr->getPositionY() - 18); - } - int levelID = level->m_levelID.value(); - if (exists(levelID)) { - int position = get(levelID); - if (position > 0) { - positionLabel->setString(fmt::format("#{}", position).c_str()); - positionLabel->setScale(getScaleBasedPos(position)); - positionLabel->setVisible(true); - demonSpr->setVisible(true); - } - } else { - this->retain(); - auto loading_circle = LoadingCircle::create(); - if (level->m_coins > 0) { - loading_circle->setPosition({-100, -2}); - loading_circle->setScale(0.3F); - } else { - loading_circle->setPosition({-100, 8}); - loading_circle->setScale(0.4F); - } - loading_circle->setParentLayer(this); - loading_circle->show(); - - const std::function)> then = [this, level, levelID, loading_circle, positionLabel, demonSpr, winSize](Result const& result_json) { - matjson::Value json = result_json.unwrapOrDefault(); - - std::string listId = Mod::get()->template getSettingValue("demonListSelection"); - - if (listId == "Pointercrate") { - if (loading_circle != nullptr) { - loading_circle->fadeAndRemove(); - } - auto scene = CCDirector::sharedDirector()->getRunningScene(); - if (json.dump() == "[]") { //idk how to check size, doing .count crashes - log::info("Level not found in Pointercrate."); - this->release(); - } else { - auto info = json.get(0); - if (info.isOk()) { - auto positionRes = info.unwrap().get("position"); - if (positionRes.isOk()) { - int position = positionRes.unwrap().asInt().unwrapOrDefault(); - positionLabel->setString(fmt::format("#{}", position).c_str()); - positionLabel->setScale(getScaleBasedPos(position)); - positionLabel->setVisible(true); - demonSpr->setVisible(true); - set(levelID, position); - log::info("Level found in Pointercrate! {} at #{}", level->m_levelName.c_str(), position); - } - } - this->release(); - - } - } else { - if (loading_circle != nullptr) { - loading_circle->fadeAndRemove(); - } - auto scene = CCDirector::sharedDirector()->getRunningScene(); - if (json.contains("code")) { - this->release(); - } else { - auto positionRes = json.get("position"); - if (positionRes.isOk()) { - int position = positionRes.unwrap().asInt().unwrapOrDefault(); - positionLabel->setString(fmt::format("#{}", position).c_str()); - positionLabel->setScale(getScaleBasedPos(position)); - positionLabel->setVisible(true); - demonSpr->setVisible(true); - set(levelID, position); - log::info("Level found in AREDL! {} at #{}", level->m_levelName.c_str(), position); - } - this->release(); - } - } - }; - const std::function expect = [this, loading_circle](std::string const& error) { - if (loading_circle != nullptr) { - loading_circle->fadeAndRemove(); - } - log::error("Error while sending a request to Demon List: {}", error); - /*FLAlertLayer::create(nullptr, - "Error", - "Failed to make a request to Demon List. Please either try again later, look at the error logs to see what might have happened, or report this to the developers.", - "OK", - nullptr, - 350.0F - )->show();*/ - this->release(); - }; - - std::string listId = Mod::get()->template getSettingValue("demonListSelection"); - std::string url = ""; - if (listId == "Pointercrate") { - url = fmt::format("https://pointercrate.com/api/v2/demons/listed/?level_id={}", levelID); - } else { - url = fmt::format("https://api.aredl.net/api/aredl/levels/{}", levelID); - } - - geode::utils::web::WebRequest request = web::WebRequest(); - const std::lock_guard lock(lock_var); - RUNNING_REQUESTS.emplace( - "@loaderDemonListLevelInfo", - request.get(url.c_str()).map( - [expect = std::move(expect), then = std::move(then)](web::WebResponse* response) { - const std::lock_guard lock(lock_var); - if (response->ok()) { - then(response->json()); - } else { - expect("Request failed"); - } - - RUNNING_REQUESTS.erase("@loaderDemonListLevelInfo"); - return *response; - } - ) - ); - } - - return true; - } -}; -// demon list diff --git a/src/Utils/DownloadManager.cpp b/src/Utils/DownloadManager.cpp deleted file mode 100644 index 9519ddd..0000000 --- a/src/Utils/DownloadManager.cpp +++ /dev/null @@ -1,112 +0,0 @@ -#include "DownloadManager.h" -#ifndef GEODE_IS_MACOS // unfortunately figs video player not supported on mac becuase opengl32 doesnt want to compile correctly koeirjnfioewbnfejwf -#include "../ProcessLambdas.h" -#include -#include -#include - -EventListener m_listener_dl; - -// ------ -// 204 = MAX -// 0 = MIN -bool ProgressBar::init() { // on purpose im not even providing any params because why would we!?; - progressBar->setPosition({3, 8}); - progressBar->setAnchorPoint({ 0, 0.5 }); - progressBar->setTextureRect(CCRect(0, 0, 204, 8)); - ccTexParams params = { GL_LINEAR, GL_LINEAR, GL_REPEAT, GL_REPEAT }; // tf is this nonsense mat - progressBar->getTexture()->setTexParameters(¶ms); - this->addChild(outerBg); - progressBar->setZOrder(-1); - outerBg->addChild(progressBar); - return true; -} - -void ProgressBar::setProgress(float percentage) { - this->m_pPercentage = percentage; - float realPercentage = (204/100) * this->m_pPercentage; // "Result of integer division used in a floating point" ok i know clangd warnings wow - progressBar->setTextureRect(CCRect(0, 0, realPercentage, 8)); -} - -ProgressBar* ProgressBar::create() { - auto pRet = new ProgressBar(); - - if (pRet && pRet->init()) { - pRet->autorelease(); - return pRet; - } - - CC_SAFE_DELETE(pRet); - return nullptr; -} -// ------ -// credits https://stackoverflow.com/a/1639047 -int DownloadManager::progress_func(void*, double totalDownload, std::optional downloadAmount, double TotalToUpload, double NowUploaded) { - if (totalDownload <= 0.0 || !downloadAmount) return 0; - float amountDownloaded = round((downloadAmount.value() / totalDownload) * 100); - auto scene = CCDirector::sharedDirector()->getRunningScene(); - if (m_progressBar != nullptr) { - m_progressBar->setProgress(amountDownloaded); - } - return 0; -} - -bool DownloadManager::setup(const char* url, const char* destination, cocos2d::SEL_MenuHandler selector) { - auto title = CCLabelBMFont::create("Downloading...", "bigFont.fnt"); - title->setScale(0.8f); - m_mainLayer->addChildAtPosition(title, Anchor::Top, {0, -25}); - - auto winSize = cocos2d::CCDirector::sharedDirector()->getWinSize(); - loading_circle->setParentLayer(this); - loading_circle->setScale(0.675f); - loading_circle->show(); - m_progressBar = ProgressBar::create(); - m_progressBar->setProgress(0.0F); - m_mainLayer->addChildAtPosition(m_progressBar, Anchor::Center, {0, -30}); - - auto text = cocos2d::CCLabelBMFont::create("Do not close this menu while the file is being downloaded.", "chatFont.fnt"); - text->setAlignment(cocos2d::kCCTextAlignmentCenter); - text->setScale(.5F); - m_mainLayer->addChildAtPosition(text, Anchor::Bottom, {0, 18}); - - // def not copied from geode hahahaha - const std::function then = [this, destination, selector](geode::ByteVector const& data) { - // Save the file - (void)geode::utils::file::writeBinary(destination, data); - - ProcessLambdas::callMenuHandler(this, selector); - this->onClose(nullptr); - }; - const std::function expect = [this](int const& error) { - FLAlertLayer::create(nullptr, "Error!", "An error occured while trying to send a request to the servers. Check logs for more information.", "OK", nullptr, 200.0F)->show(); - log::error("Error downloading: {}", error); - }; - - geode::utils::web::WebRequest request = web::WebRequest(); - m_listener_dl.bind([this, expect = std::move(expect), then = std::move(then)] (web::WebTask::Event* e) { - if (web::WebProgress* progress = e->getProgress()) { - progress_func(NULL, 100, progress->downloadProgress(), progress->uploadTotal(), progress->uploaded()); - } else if (web::WebResponse* result = e->getValue()) { - if (result->ok()) { - then(result->data()); - } else { - expect(result->code()); - } - } - }); - - m_listener_dl.setFilter(request.get(url)); - return true; -} - -DownloadManager* DownloadManager::create(const char* url, const char* destination, cocos2d::SEL_MenuHandler selector) { - auto ret = new DownloadManager(); - if (ret->initAnchored(300.f, 150.f, url, destination, selector)) { - ret->autorelease(); - return ret; - } - delete ret; - return nullptr; -} -// below is copied from the gdr mod lol -#endif diff --git a/src/Utils/DownloadManager.h b/src/Utils/DownloadManager.h deleted file mode 100644 index 425d73d..0000000 --- a/src/Utils/DownloadManager.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once -// windows only because i cant get this to work, actually im too lazy -#ifndef GEODE_IS_MACOS -#include -using namespace geode::prelude; -class ProgressBar : public CCNode { - protected: - float m_pPercentage = 0; - CCSprite* outerBg = CCSprite::create("slidergroove.png"); - CCSprite* progressBar = CCSprite::create("sliderBar.png"); - bool init(); - public: - void testProgressbar(); - virtual void setProgress(float percentage); - static ProgressBar* create(); -}; -class DownloadManager : public geode::Popup { - LoadingCircle* loading_circle = LoadingCircle::create(); - protected: - bool setup(const char* url, const char* destination, cocos2d::SEL_MenuHandler selector); - ProgressBar* m_progressBar; - public: - static constexpr const float s_defWidth = 300.0f; - static constexpr const float s_defHeight = 180.0f; - static DownloadManager* create(const char* url, const char* destination, cocos2d::SEL_MenuHandler callback); //, cocos2d::SEL_MenuHandler callback - - // Downloader class - int progress_func(void*, double, std::optional, double, double); -}; -#endif diff --git a/src/Utils/FriendsProfilePage.cpp b/src/Utils/FriendsProfilePage.cpp deleted file mode 100644 index ccf9fbd..0000000 --- a/src/Utils/FriendsProfilePage.cpp +++ /dev/null @@ -1,216 +0,0 @@ -#include "../includes.h" -#include -// Utils - -// Function to convert a const char* to lowercase -const char* toLowerCase(const char* str) { - char* result = new char[strlen(str) + 1]; - size_t i = 0; - - while (str[i]) { - result[i] = std::tolower(str[i]); - i++; - } - - result[i] = '\0'; - return result; -} - -class SearchUserLayer : public Popup<> { - protected: - bool setup() { - input_username->setMaxCharCount(20); - input_username->setPlaceholder(""); - input_username->setPositionY(10); - - m_buttonMenu->addChildAtPosition(input_username, Anchor::Center); - auto winSize = cocos2d::CCDirector::sharedDirector()->getWinSize(); - auto validate_spr = ButtonSprite::create("Search", 60, true, "bigFont.fnt", "GJ_button_01.png", 30, .5F); - auto validate_btn = CCMenuItemSpriteExtra::create( - validate_spr, - this, - menu_selector(SearchUserLayer::onValidate) - ); - m_buttonMenu->addChildAtPosition(validate_btn, Anchor::Center, {0, -35}); - return true; - } - void onValidate(cocos2d::CCObject*); - public: - TextInput* input_username = TextInput::create(200.0F, "Username", "bigFont.fnt"); - static SearchUserLayer* create() { - auto ret = new SearchUserLayer(); - if (ret->initAnchored(260.f, 60.f)) { - ret->autorelease(); - return ret; - } - delete ret; - return nullptr; - } -}; - -class $modify(FriendPage, FriendsProfilePage) { - bool init(UserListType type) { - // If the base init fails, stop here - if (!FriendsProfilePage::init(type)) return false; - // If the setting is disabled, stop here - if (!Mod::get()->template getSettingValue("friendSearch")) return true; - - auto menu = this->m_buttonMenu; - - auto searchSpr = CCSprite::createWithSpriteFrameName("gj_findBtn_001.png"); - searchSpr->setScale(1.225F); - auto searchBtn = CCMenuItemSpriteExtra::create( - searchSpr, - this, - menu_selector(FriendPage::onSearch) - ); - searchBtn->setPosition(320, 0); - menu->addChild(searchBtn); - return true; - } - - void onSearch(CCObject*) { - SearchUserLayer::create()->show(); - } - - static TableView* getCustomList(CCArray* sceneChildren) { - CCLayer* test1 = typeinfo_cast(misc::findNode("FriendsProfilePage")); - if (test1 == nullptr) { - // safeguard from crashing - FLAlertLayer::create(nullptr, - "Error", - "The mod could not find the FriendsProfilePage layer. Please either try again later, removing mods that may be interfering with the scene, or report this to the developers.", - "OK", - nullptr, - 350.0F - )->show(); - return nullptr; - } - test1 = typeinfo_cast(test1->getChildren()->objectAtIndex(0)); - CCLayer* test2 = nullptr; - for (int i = 0; i < test1->getChildrenCount(); i++) { - if (misc::getNodeName(test1->getChildren()->objectAtIndex(i)) == "GJCommentListLayer") { - test2 = static_cast(test1->getChildren()->objectAtIndex(i)); - break; - } - } - - // If we couldn't find the comment list layer, return - if (test2 == nullptr) { - // safeguard from crashing - FLAlertLayer::create(nullptr, - "Error", - "The mod could not find the GJCommentListLayer layer. Please either try again later, removing mods that may be interfering with the scene, or report this to the developers.", - "OK", - nullptr, - 350.0F - )->show(); - return nullptr; - } - - auto test3 = static_cast(test2->getChildren()->objectAtIndex(0)); - - // If you don't have friend, show alert (sorry for you) - if (test3->getChildrenCount() <= 0) { - // another safeguard - FLAlertLayer::create(nullptr, - "Error", - "You have no friends!", - "OK", - nullptr, - 200.0F - )->show(); - return nullptr; - } - - return static_cast(test3->getChildren()->objectAtIndex(0)); - } - - static void searchUser(const char* username) { - auto scene = CCDirector::sharedDirector()->getRunningScene(); - auto sceneChildren = scene->getChildren(); - auto customList = getCustomList(sceneChildren); - - // If the list is null, return - if (customList == nullptr) return; - - CCContentLayer* contentLayer = static_cast( - customList->getChildren()->objectAtIndex(0) - ); - - int counter_page = 0; - bool found = false; - - // Iterate through all cells in the list - for (int i = 0; i < contentLayer->getChildrenCount(); i++) { - GJUserCell* cell = static_cast( - contentLayer->getChildren()->objectAtIndex(i) - ); - - const char* str1 = cell->m_userScore->m_userName.c_str(); - - // If username is found in the cell's username (case insensitive) - if (strstr(toLowerCase(str1), toLowerCase(username)) != nullptr) { - customList->scrollLayer(-9999999); - customList->scrollLayer(counter_page); - - found = true; - - // Pulse only one time cell color animation - auto originalColor = cell->getChildByType(0)->getColor(); - auto tintTo1 = CCTintTo::create(0.2f, 0, 255, 0); // Change to green - auto tintTo2 = CCTintTo::create(0.5f, originalColor.r, originalColor.g, originalColor.b); // Change back to original color - auto sequence = CCSequence::create(tintTo1, tintTo2, nullptr); - cell->getChildByType(0)->runAction(sequence); - - break; - } - counter_page += 45; - } - - // If user not found, show alert - if (!found) { - std::string str = username; - FLAlertLayer::create(nullptr, - "Error", - "User not found : " + str, - "OK", - nullptr, - 200.0F - )->show(); - } - } - - void setupUsersBrowser(cocos2d::CCArray * users, UserListType type) - { - FriendsProfilePage::setupUsersBrowser(users, type); - - if (m_buttonMenu->getChildByID("friendsScrollBar")) - { - m_buttonMenu->removeChildByID("friendsScrollBar"); - } - } - - virtual void getUserListFinished(cocos2d::CCArray* a1, UserListType a2) { - FriendsProfilePage::getUserListFinished(a1,a2); - - auto menu = this->m_buttonMenu; - - // Add custom scrollbar - auto scrollBar = Scrollbar::create(this->m_listLayer->m_list->m_tableView); - scrollBar->setPosition(390, -140); - scrollBar->setID("friendsScrollBar"); - menu->addChild(scrollBar); - } - - virtual void onUpdate(UserListType a1) { - FriendsProfilePage::forceReloadList(a1); - - m_buttonMenu->removeChildByID("friendsScrollBar"); - } -}; - -void SearchUserLayer::onValidate(CCObject* pSender) { - FriendPage::searchUser(input_username->getString().c_str()); - Popup::onClose(pSender); -} diff --git a/src/Utils/InfoNewLayer.cpp b/src/Utils/InfoNewLayer.cpp deleted file mode 100644 index ef33337..0000000 --- a/src/Utils/InfoNewLayer.cpp +++ /dev/null @@ -1,39 +0,0 @@ -#include "InfoNewLayer.h" -#include - -void InfoNewLayer::onCopyLevelURL(CCObject* pSender) { - std::string level = static_cast(static_cast(pSender)->getUserObject())->getCString(); - utils::clipboard::write("https://gdutils.com/" + level); - - auto notification = Notification::create("Copied level URL to clipboard!", NotificationIcon::Success); - notification->setZOrder(1000); - notification->setTime(0.2f); - notification->show(); -} - -// Copy button for sharing levels -class $modify(InfoLayer) { - bool init(GJGameLevel* level, GJUserScore* userscore, GJLevelList* levellist) { - if (!InfoLayer::init(level, userscore, levellist)) return false; - - // Check if it's InfoLayer for level - if (level == nullptr) return true; - if (!Mod::get()->template getSettingValue("levelsProtocolButton")) return true; - - std::string levelID = std::to_string(level->m_levelID.value()); - - CCMenu* menu = typeinfo_cast(this->getChildByIDRecursive("left-side-menu")); - auto copySpr = CCSprite::createWithSpriteFrameName("GJ_redoBtn_001.png"); - copySpr->setScale(.7f); - auto copyBtn = CCMenuItemSpriteExtra::create( - copySpr, - this, - menu_selector(InfoNewLayer::onCopyLevelURL) - ); - copyBtn->setUserObject(cocos2d::CCString::create(gd::string(levelID))); - copyBtn->setPosition(14, -55); - menu->addChild(copyBtn); - - return true; - } -}; diff --git a/src/Utils/InfoNewLayer.h b/src/Utils/InfoNewLayer.h deleted file mode 100644 index 1ec5b49..0000000 --- a/src/Utils/InfoNewLayer.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once -#include -#include -using namespace geode::prelude; - -class InfoNewLayer : public CCLayer { - public: - void onCopyLevelURL(CCObject* pSender); -}; diff --git a/src/Utils/ParticleManager.hpp b/src/Utils/ParticleManager.hpp deleted file mode 100644 index 04c400a..0000000 --- a/src/Utils/ParticleManager.hpp +++ /dev/null @@ -1,56 +0,0 @@ -#pragma once -#include - -using namespace geode::prelude; - -// Code took from ItzKiba/BetterProgression, thanks! - -inline static CCParticleSystem* createParticles(int numParticles, ccColor3B startColor, ccColor3B endColor, std::string emitterType, std::string duration, std::string particleLifespan, std::string particleLifespanVariance, std::string angle, std::string angleVariance, std::string sourcePositionx, std::string sourcePositiony, std::string sourcePositionVariancex, std::string sourcePositionVariancey, std::string maxRadius, std::string minRadius, std::string startParticleSize, std::string finishParticleSize, std::string startParticleSizeVariance, std::string speed, std::string speedVariance, std::string gravityx, std::string gravityy, std::string startColorAlpha, std::string startColorVarianceRed, std::string startColorVarianceBlue, std::string startColorVarianceGreen, std::string startColorVarianceAlpha, std::string finishColorAlpha, std::string finishColorVarianceRed, std::string finishColorVarianceBlue, std::string finishColorVarianceGreen, std::string finishColorVarianceAlpha, std::string rotatePerSecond, std::string rotatePerSecondVariance, std::string rotationEndVariance, std::string textureFileName) { - auto dict = CCDictionary::createWithContentsOfFileThreadSafe("dragEffect.plist"); - - dict->setObject(CCString::create(emitterType), "emitterType"); - dict->setObject(CCString::create(duration), "duration"); - dict->setObject(CCString::createWithFormat("%i", numParticles), "maxParticles"); - dict->setObject(CCString::create(particleLifespan), "particleLifespan"); - dict->setObject(CCString::create(particleLifespanVariance), "particleLifespanVariance"); - dict->setObject(CCString::create(angle), "angle"); - dict->setObject(CCString::create(angleVariance), "angleVariance"); - dict->setObject(CCString::create(sourcePositionx), "sourcePositionx"); - dict->setObject(CCString::create(sourcePositiony), "sourcePositiony"); - dict->setObject(CCString::create(sourcePositionVariancex), "sourcePositionVariancex"); - dict->setObject(CCString::create(sourcePositionVariancey), "sourcePositionVariancey"); - dict->setObject(CCString::create(maxRadius), "maxRadius"); - dict->setObject(CCString::create(minRadius), "minRadius"); - dict->setObject(CCString::create(startParticleSize), "startParticleSize"); - dict->setObject(CCString::create(finishParticleSize), "finishParticleSize"); - dict->setObject(CCString::create(startParticleSizeVariance), "startParticleSizeVariance"); - dict->setObject(CCString::create(speed), "speed"); - dict->setObject(CCString::create(speedVariance), "speedVariance"); - dict->setObject(CCString::create(gravityx), "gravityx"); - dict->setObject(CCString::create(gravityy), "gravityy"); - dict->setObject(CCString::create(fmt::format("{}", startColor.r)), "startColorRed"); - dict->setObject(CCString::create(fmt::format("{}", startColor.g)), "startColorGreen"); - dict->setObject(CCString::create(fmt::format("{}", startColor.b)), "startColorBlue"); - dict->setObject(CCString::create(startColorAlpha), "startColorAlpha"); - dict->setObject(CCString::create(startColorVarianceRed), "startColorVarianceRed"); - dict->setObject(CCString::create(startColorVarianceBlue), "startColorVarianceBlue"); - dict->setObject(CCString::create(startColorVarianceGreen), "startColorVarianceGreen"); - dict->setObject(CCString::create(startColorVarianceAlpha), "startColorVarianceAlpha"); - dict->setObject(CCString::create(fmt::format("{}", endColor.r)), "finishColorRed"); - dict->setObject(CCString::create(fmt::format("{}", endColor.g)), "finishColorGreen"); - dict->setObject(CCString::create(fmt::format("{}", endColor.b)), "finishColorBlue"); - dict->setObject(CCString::create(finishColorAlpha), "finishColorAlpha"); - dict->setObject(CCString::create(finishColorVarianceRed), "finishColorVarianceRed"); - dict->setObject(CCString::create(finishColorVarianceBlue), "finishColorVarianceBlue"); - dict->setObject(CCString::create(finishColorVarianceGreen), "finishColorVarianceGreen"); - dict->setObject(CCString::create(finishColorVarianceAlpha), "finishColorVarianceAlpha"); - dict->setObject(CCString::create(rotatePerSecond), "rotatePerSecond"); - dict->setObject(CCString::create(rotatePerSecondVariance), "rotatePerSecondVariance"); - dict->setObject(CCString::create(rotationEndVariance), "rotationEndVariance"); - dict->setObject(CCString::create(textureFileName), "textureFileName"); - - auto emitter = CCParticleSystemQuad::create(); - emitter->initWithDictionary(dict, false); - - return emitter; -} \ No newline at end of file diff --git a/src/config.hpp b/src/config.hpp new file mode 100644 index 0000000..83f2773 --- /dev/null +++ b/src/config.hpp @@ -0,0 +1,6 @@ +#pragma once +#define MAX_RECONNECT_ATTEMPTS 20 +#define AMQ_HOST "gdutils.clarifygdps.com" +#define AMQ_PORT 5672 +#define AMQ_USER "gd" +#define AMQ_PASS "GeometryDashisahorizontalrunnerstylegamedevelopedandpublishedbyRobTopGames" diff --git a/src/defs/geode.hpp b/src/defs/geode.hpp new file mode 100644 index 0000000..5d2802d --- /dev/null +++ b/src/defs/geode.hpp @@ -0,0 +1,5 @@ +#pragma once +#include +#include + +using namespace geode::prelude; diff --git a/src/defs/misc.hpp b/src/defs/misc.hpp new file mode 100644 index 0000000..4cfc11c --- /dev/null +++ b/src/defs/misc.hpp @@ -0,0 +1,36 @@ +#pragma once +#include +#include +#include "geode.hpp" + +namespace misc { + // theres no including Geode Util class funcs so, https://github.com/geode-sdk/DevTools + /*static + static CCObject* findNode(std::string name) { + auto scene = CCDirector::sharedDirector()->getRunningScene(); + auto sceneChildren = scene->getChildren(); + for (unsigned int i = 0; i < scene->getChildrenCount(); i++) { + auto node = sceneChildren->objectAtIndex(i); + if (node != nullptr) { + std::string layerName = misc::getNodeName(node); + std::cout << layerName << std::endl; + if (layerName == name) { + return node; + } + } + } + return nullptr; + }*/ + template + std::vector> paginate(const std::vector& arr, size_t size) { + std::vector> paginated; + for (size_t i = 0; i < arr.size(); i += size) { + std::vector page; + for (size_t j = i; j < std::min(i + size, arr.size()); ++j) { + page.push_back(arr[j]); + } + paginated.push_back(page); + } + return paginated; + } +}; diff --git a/src/hooks/LevelInfoLayer.cpp b/src/hooks/LevelInfoLayer.cpp new file mode 100644 index 0000000..c1dab01 --- /dev/null +++ b/src/hooks/LevelInfoLayer.cpp @@ -0,0 +1,175 @@ +#include +#include +#include +#include + +// demon list +std::unordered_map demonListCache; // Will clear after game exit, or if user deletes level + +class $modify(LevelInfoLayer) { + struct Fields { + async::TaskHolder m_listener; + }; + void set(int key, int value) { + demonListCache[key] = value; + } + bool exists(int key) { + return demonListCache.find(key) != demonListCache.end(); + } + int get(int key) { + auto it = demonListCache.find(key); + if (it != demonListCache.end()) { + return it->second; + } + return -1; + } + bool remove(int key) { + return demonListCache.erase(key) == 1; + } + float getScaleBasedPos(int pos) { + if (pos > 0 && pos < 10) return 0.5F; + if (pos > 10 && pos < 100) return 0.4F; + if (pos > 100 && pos < 1000) return 0.3F; + if (pos > 1000 && pos < 10000) return 0.2F; + return 0.1F; + } + bool init(GJGameLevel* level, bool p1) { // inspiration le gdbrowser, what does p1 do? idk + if (!LevelInfoLayer::init(level, p1)) return false; + if (level->m_demon.value() == 0 || level->m_stars.value() != 10) return true; + if (level->m_demonDifficulty != 6) return true; + + auto director = CCDirector::sharedDirector(); + auto winSize = director->getWinSize(); + auto demonSpr = Build::createSpriteName("diffIcon_06_btn_001.png") + .pos({ + (winSize.width / 2) - 85, + (level->m_coins > 0) ? (winSize.height / 2) : (winSize.height / 2) + 18 + }) + .scale(0.5F) + .id("demon-spr"_spr) + .visible(false) + .parent(this) + .collect(); + auto positionLabel = Build::create("N/A", "bigFont.fnt") + .pos({ + (winSize.width / 2) - 107, + (level->m_coins > 0) ? (winSize.height / 2) : (winSize.height / 2) + 18 + }) + .scale(0.5F) + .id("demon-pos"_spr) + .visible(false) + .parent(this) + .collect(); + int levelID = level->m_levelID.value(); + if (exists(levelID)) { + int position = get(levelID); + if (position > 0) { + positionLabel->setString(fmt::format("#{}", position).c_str()); + positionLabel->setScale(getScaleBasedPos(position)); + positionLabel->setVisible(true); + demonSpr->setVisible(true); + } + } else { + auto loading_circle = LoadingCircle::create(); + if (level->m_coins > 0) { + loading_circle->setPosition({-100, -2}); + loading_circle->setScale(0.3F); + } else { + loading_circle->setPosition({-100, 8}); + loading_circle->setScale(0.4F); + } + loading_circle->setParentLayer(this); + loading_circle->show(); + std::string listId = Mod::get()->getSettingValue("demonListSelection"); + std::string url = ""; + if (listId == "Pointercrate") { + url = fmt::format("https://pointercrate.com/api/v2/demons/listed/?level_id={}", levelID); + } else { + url = fmt::format("https://api.aredl.net/api/aredl/levels/{}", levelID); + } + geode::utils::web::WebRequest request = web::WebRequest(); + m_fields->m_listener.spawn("@loaderDemonListLevelInfo", request.get(url.c_str()), [this, loading_circle, positionLabel, demonSpr, level, levelID](web::WebResponse value) { + if (value.ok()) { + matjson::Value json = value.json().unwrapOrDefault(); + + std::string listId = Mod::get()->template getSettingValue("demonListSelection"); + + if (listId == "Pointercrate") { + if (loading_circle != nullptr) { + loading_circle->fadeAndRemove(); + } + auto scene = CCDirector::sharedDirector()->getRunningScene(); + if (json.dump() == "[]") { //idk how to check size, doing .count crashes + log::info("Level not found in Pointercrate."); + } else { + auto info = json.get(0); + if (info.isOk()) { + auto positionRes = info.unwrap().get("position"); + if (positionRes.isOk()) { + int position = positionRes.unwrap().asInt().unwrapOrDefault(); + positionLabel->setString(fmt::format("#{}", position).c_str()); + positionLabel->setScale(getScaleBasedPos(position)); + positionLabel->setVisible(true); + demonSpr->setVisible(true); + set(levelID, position); + log::info("Level found in Pointercrate! {} at #{}", level->m_levelName.c_str(), position); + } + } + + } + } else { + if (loading_circle != nullptr) { + loading_circle->fadeAndRemove(); + } + auto scene = CCDirector::sharedDirector()->getRunningScene(); + if (json.contains("code")) { + } else { + auto positionRes = json.get("position"); + if (positionRes.isOk()) { + int position = positionRes.unwrap().asInt().unwrapOrDefault(); + positionLabel->setString(fmt::format("#{}", position).c_str()); + positionLabel->setScale(getScaleBasedPos(position)); + positionLabel->setVisible(true); + demonSpr->setVisible(true); + set(levelID, position); + log::info("Level found in AREDL! {} at #{}", level->m_levelName.c_str(), position); + } + } + } + } else { + if (loading_circle != nullptr) { + loading_circle->fadeAndRemove(); + } + log::error("Error while sending a request to Demon List: {}", value.errorMessage()); + } + }); + } + return true; + } +}; +// demon list + +void toggleHook(bool value) { + for (auto& hook : geode::Mod::get()->getHooks()) { + if (hook->getDisplayName() == "LevelInfoLayer::init") { + if (value) { + (void)hook->enable(); + } else { + (void)hook->disable(); + } + } + } +} + +$execute { + listenForSettingChanges("demonListPlacement", [](bool value) { + log::info("Demon List Placement changed to {}", value); + toggleHook(value); + }); +} + +$on_mod(Loaded) { + if (!Mod::get()->getSettingValue("demonListPlacement")) { + toggleHook(false); + } +} diff --git a/src/hooks/MenuLayer.cpp b/src/hooks/MenuLayer.cpp new file mode 100644 index 0000000..143bb62 --- /dev/null +++ b/src/hooks/MenuLayer.cpp @@ -0,0 +1,97 @@ +#include +#include +#include +#include +#include + +bool is_socketserver_started = false; +class $modify(MenuLayer) { + bool init() { + if (!MenuLayer::init()) return false; + return true; + + /*if (!is_dailychest_ready) { + std::thread hThread(dailyChestThread); + hThread.detach(); + is_dailychest_ready = true; + }*/ + if (!is_socketserver_started) { + bool startSocketServer = Mod::get()->template getSettingValue("socketServer"); + if (startSocketServer) { + if (!Mod::get()->hasSavedValue("clientId")) { + // this is just for statistics ok + #ifdef GEODE_IS_WINDOWS + std::string platform = "Windows"; + #elif defined(GEODE_IS_ANDROID) + std::string platform = "Android"; + #elif defined(GEODE_IS_MACOS) + std::string platform = "Mac"; + #elif defined(GEODE_IS_IOS) + std::string platform = "iOS"; + #else + std::string platform = "Unknown"; + #endif + + const std::string charset = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution<> dist(0, charset.size() - 1); + + std::string randomStr; + for (size_t i = 0; i < 8; ++i) { + randomStr.push_back(charset[dist(gen)]); + } + auto now = std::chrono::system_clock::now(); + auto now_ms = std::chrono::duration_cast(now.time_since_epoch()).count(); + int accountID = 0; + if (auto gjam = GJAccountManager::get()) { + accountID = gjam->m_accountID; + } + Mod::get()->setSavedValue("clientId", fmt::format("gdutils-{}-{}-{}-{}", accountID, platform, now_ms, randomStr)); + } + if (!Mod::get()->hasSavedValue("clientId")) { + Notification::create("Couldn't connect to server, invalid Client ID", NotificationIcon::Error)->show(); + return true; + } + /*auto [tx, rx] = arc::mpsc::channel(1024); // i mean why would robtop ever rate that many levels + async::runtime().spawn([rx = std::move(rx)]() mutable -> arc::Future<> { + while (true) { + auto opt = co_await rx.recv(); + if (!opt) { + co_return; + } + std::string msg = std::move(opt).unwrap(); + + Loader::get()->queueInMainThread([m = std::move(msg)]() mutable { + auto director = CCDirector::sharedDirector(); + if (!director) return; + auto scene = CCDirector::sharedDirector()->getRunningScene(); + if (!scene) return; + if (scene->getChildrenCount() == 0) return; + EventHandler::aaa(); + }); + } + co_return; + }); + auto director = CCDirector::sharedDirector(); + + if (director) { + auto scene = CCDirector::sharedDirector()->getRunningScene(); + if (scene && scene->getChildrenCount() > 0) { + //arc::spawn(EventHandler::consumer(std::move(rx), scene)); + } + }*/ + //arc::spawn(EventHandler::eventLoop(scene, std::move(rx))); + //[tx = std::move(tx) + std::thread hThread([]() mutable { + AMQT handler(Mod::get()->getSavedValue("clientId")); + handler.connect(); + }); + hThread.detach(); + } + is_socketserver_started = true; + } + return true; + } +}; + diff --git a/src/Secret/BadApple.cpp b/src/hooks/SecretLayer2.cpp similarity index 55% rename from src/Secret/BadApple.cpp rename to src/hooks/SecretLayer2.cpp index 3297a51..b199779 100644 --- a/src/Secret/BadApple.cpp +++ b/src/hooks/SecretLayer2.cpp @@ -1,99 +1,43 @@ -// SPOILERSSS -#include "BadApple.h" -#ifdef GEODE_IS_WINDOWS -#include "../Utils/DownloadManager.h" -#else // mac -#include -#include -#endif -BadApple* BadApple::create() { - auto pRet = new BadApple(); - if (pRet && pRet->BadApple::init()) { - pRet->autorelease(); - return pRet; - } - CC_SAFE_DELETE(pRet); - return nullptr; -}; - -bool BadApple::init() { - auto menu = CCMenu::create(); - //#ifndef GEODE_IS_MACOS - #ifdef GEODE_IS_WINDOWS - videoplayer::VideoPlayer* player = videoplayer::VideoPlayer::create("./gdutils/bad_apple.mpg", true); - CCDirector* director = CCDirector::sharedDirector(); - CCSize size = director->getWinSize(); - player->setPosition(size.width / 2, size.height / 2); - player->setContentSize({ size.width - 40, size.height - 40 }); - player->setZOrder(-1); - this->addChild(player); - #endif - setKeypadEnabled(true); - - return true; -} - -void BadApple::keyBackClicked() { - setKeypadEnabled(false); - - #ifdef GEODE_IS_UNKNOWN - GameSoundManager::sharedManager()->playBackgroundMusic("./Resources/secretLoop02.mp3", true, false); - #endif - CCDirector::sharedDirector()->popSceneWithTransition(0.5f, PopTransition::kPopTransitionFade); -} - // touhou time -//#ifdef GEODE_IS_WINDOWS -std::string text_input = ""; -#include "BadApple.h" -bool bad_apple = false; -#include "COTE.h" -bool cote = false; - #include +#include +#include +#include +#include + +enum States { + None, + BadApple, + COTE +}; class $modify(SecretVault, SecretLayer2) { struct Fields { CCMenu* m_menu = nullptr; + States m_currentState = States::None; }; bool init() { if (!SecretLayer2::init()) return false; - bad_apple = false; - cote = false; return true; } void onSubmit(CCObject* obj) { + gd::string input = m_searchInput->getString(); SecretLayer2::onSubmit(obj); - if (m_fields->m_menu == nullptr) { - m_fields->m_menu = CCMenu::create(); - m_fields->m_menu->setPosition(50, 200); - m_fields->m_menu->setContentSize({ 100, 200 }); - m_fields->m_menu->setLayout( - ColumnLayout::create() - ->setCrossAxisAlignment(AxisAlignment::Center) - ); + Build::create().pos(50, 200).contentSize({100, 200}).layout( + ColumnLayout::create()->setCrossAxisAlignment(AxisAlignment::Center) + ).store(m_fields->m_menu).parent(this); } - - this->addChild(m_fields->m_menu); - m_fields->m_menu->removeAllChildren(); - - CCLabelBMFont* vault_text = nullptr; - if (this->getChildByIDRecursive("vault-text") == nullptr) { - vault_text = typeinfo_cast(this->getChildren()->objectAtIndex(3)); - } else { - vault_text = as(this->getChildByIDRecursive("vault-text")); - vault_text->setID("vault-text"); + CCLabelBMFont* vault_text = typeinfo_cast(this->getChildByIDRecursive("vault-text")); + if (!vault_text) { + vault_text = this->getChildByType(1); } - - if (!strcmp(text_input.c_str(), "bad apple")) { - if (!bad_apple) { - std::filesystem::create_directory("gdutils"); - bad_apple = true; - cote = false; - + log::debug("what are you {}", input); + if (!strcmp(input.c_str(), "bad apple")) { + if (m_fields->m_currentState != States::BadApple) { + m_fields->m_currentState = States::BadApple; auto menu = CCMenu::create(); menu->setLayout( RowLayout::create() @@ -127,12 +71,9 @@ class $modify(SecretVault, SecretLayer2) { menu->updateLayout(); m_fields->m_menu->addChild(menu); } - } else if (!strcmp(text_input.c_str(), "honami best girl")) { - if (!cote) { - std::filesystem::create_directory("gdutils"); - cote = true; - bad_apple = false; - + } else if (!strcmp(input.c_str(), "honami best girl")) { + if (m_fields->m_currentState != States::COTE) { + m_fields->m_currentState = States::COTE; auto menu = CCMenu::create(); menu->setLayout( RowLayout::create() @@ -167,15 +108,12 @@ class $modify(SecretVault, SecretLayer2) { m_fields->m_menu->addChild(menu); } } else { - cote = false; - bad_apple = false; + m_fields->m_currentState = States::None; } - m_fields->m_menu->updateLayout(); } - void launchBadApple(CCObject* pSender) { - + void launchBadApple() { //GameSoundManager::sharedManager()->stopBackgroundMusic(); FMODAudioEngine::sharedEngine()->stopAllMusic(true); auto scene = cocos2d::CCScene::create(); @@ -185,19 +123,19 @@ class $modify(SecretVault, SecretLayer2) { } void onLaunchBadApple(CCObject* pSender) { - if (std::filesystem::exists("gdutils/bad_apple.mpg")) { - auto node = CCNode::create(); - node->setTag(10); - SecretVault::launchBadApple(node); + if (std::filesystem::exists(Mod::get()->getSaveDir() / "bad_apple.mpg")) { + SecretVault::launchBadApple(); } else { log::info("wait for bad apple"); #ifdef GEODE_IS_WINDOWS - DownloadManager::create("https://clarifygdps.com/videos/bad_apple.mpg", "gdutils/bad_apple.mpg", menu_selector(SecretVault::launchBadApple))->show(); + DownloadPopup::create("https://clarifygdps.com/videos/bad_apple.mpg", Mod::get()->getSaveDir() / "bad_apple.mpg", [this]() { + SecretVault::launchBadApple(); + })->show(); #endif } } - void launchCOTE(CCObject* pSender) { + void launchCOTE() { //GameSoundManager::sharedManager()->stopBackgroundMusic(); FMODAudioEngine::sharedEngine()->stopAllMusic(true); auto scene = cocos2d::CCScene::create(); @@ -207,23 +145,20 @@ class $modify(SecretVault, SecretLayer2) { } void onLaunchCOTE(CCObject* pSender) { - if (std::filesystem::exists("gdutils/cote.mpg")) { - auto node = CCNode::create(); - node->setTag(10); - SecretVault::launchCOTE(node); + if (std::filesystem::exists(Mod::get()->getSaveDir() / "cote.mpg")) { + SecretVault::launchCOTE(); } else { log::info("wait for cote"); #ifdef GEODE_IS_WINDOWS - DownloadManager::create("https://clarifygdps.com/videos/cote.mpg", "gdutils/cote.mpg", menu_selector(SecretVault::launchCOTE))->show(); + DownloadPopup::create("https://clarifygdps.com/videos/cote.mpg", Mod::get()->getSaveDir() / "cote.mpg", [this]() { + SecretVault::launchCOTE(); + })->show(); #endif } + } void updateSearchLabel(const char* text) { - if (strlen(text) > 0) { - text_input = text; - } SecretLayer2::updateSearchLabel(text); } }; -// touhou time diff --git a/src/includes.h b/src/includes.h deleted file mode 100644 index 8c467a4..0000000 --- a/src/includes.h +++ /dev/null @@ -1,94 +0,0 @@ -#pragma once -#include -#include -using namespace geode::prelude; - -#include -#include -#include -#include - -#define OPT(expr) if (auto __opt__ = expr) __opt__ - -class misc { - public: - // theres no including Geode Util class funcs so, https://github.com/geode-sdk/DevTools - static std::string getNodeName(cocos2d::CCObject* node) { -#ifdef GEODE_IS_WINDOWS - return typeid(*node).name() + 6; -#else - { - std::string ret; - - int status = 0; - auto demangle = abi::__cxa_demangle(typeid(*node).name(), 0, 0, &status); - if (status == 0) { - ret = demangle; - } - free(demangle); - - return ret; - } -#endif - } - static CCObject* findNode(std::string name) { - auto scene = CCDirector::sharedDirector()->getRunningScene(); - auto sceneChildren = scene->getChildren(); - for (unsigned int i = 0; i < scene->getChildrenCount(); i++) { - auto node = sceneChildren->objectAtIndex(i); - if (node != nullptr) { - std::string layerName = misc::getNodeName(node); - std::cout << layerName << std::endl; - if (layerName == name) { - return node; - } - } - } - return nullptr; - } - template - static std::vector> paginate(const std::vector& arr, size_t size) { - std::vector> paginated; - for (size_t i = 0; i < arr.size(); i += size) { - std::vector page; - for (size_t j = i; j < std::min(i + size, arr.size()); ++j) { - page.push_back(arr[j]); - } - paginated.push_back(page); - } - return paginated; - } - - static std::string genClientID() { - // this is just for statistics ok - #ifdef GEODE_IS_WINDOWS - std::string platform = "Windows"; - #elif defined(GEODE_IS_ANDROID) - std::string platform = "Android"; - #elif defined(GEODE_IS_MACOS) - std::string platform = "Mac"; - #elif defined(GEODE_IS_IOS) - std::string platform = "iOS"; - #else - std::string platform = "Unknown"; - #endif - - const std::string charset = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; - std::random_device rd; - std::mt19937 gen(rd()); - std::uniform_int_distribution<> dist(0, charset.size() - 1); - - std::string randomStr; - for (size_t i = 0; i < 8; ++i) { - randomStr.push_back(charset[dist(gen)]); - } - - auto now = std::chrono::system_clock::now(); - auto now_ms = std::chrono::duration_cast(now.time_since_epoch()).count(); - int accountID = 0; - if (auto gjam = GJAccountManager::get()) { - accountID = gjam->m_accountID; - } - return fmt::format("gdutils-{}-{}-{}-{}", accountID, platform, now_ms, randomStr); - } -}; diff --git a/src/main.cpp b/src/main.cpp index 8a94aab..5274eda 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2,850 +2,109 @@ // Ported with help by Firee // Mod made by Jouca & Firee -#include -#include -#include +$execute { + // Loader::get()->queueInMainThread([]{ + // CCScheduler::get()->scheduleSelector(schedule_selector(EventHandler::check), EventHandler::create(), 1.0F, false); + // //CCScheduler::get()->scheduleUpdateForTarget(EventHandler::create(), Priority::Last, false); + // }); + // listenForSettingChanges("activate-background", [](bool value) { + // log::info("Activate Background changed to {}", value); + // toggleSpriteHooks(value); + // }); +} +#include +#include #include -#include -#include -#include "includes.h" -#include "Settings/CustomSettings.hpp" -#include "Notifications/EventsPush.h" -#include "ProcessLambdas.h" -#include -#include -#include -#include #include -#include -#include -#include -#include -#include "Notifications/DailyChest.h" -#include - -bool show_connected = false; - -#ifdef GEODE_IS_WINDOWS // stop the dumb compiler errors! -#include -#include -//#include -#endif - -//#include -//#include -//#include - -// ah yes, lets use the raw version of the library instead of the c++ one because IT DOESNT IMPLEMENT SOCKETS -#include -#include - -//#include - -std::queue dataQueue; -std::queue chestQueue; - -std::queue msgQueue; - -std::string AMQReplyToString(amqp_response_type_enum val) { - switch (val) { - case AMQP_RESPONSE_NONE: - default: - return "AMQP_RESPONSE_NONE"; - case AMQP_RESPONSE_NORMAL: - return "AMQP_RESPONSE_NORMAL"; - case AMQP_RESPONSE_LIBRARY_EXCEPTION: - return "AMQP_RESPONSE_LIBRARY_EXCEPTION"; - case AMQP_RESPONSE_SERVER_EXCEPTION: - return "AMQP_RESPONSE_SERVER_EXCEPTION"; - } -} -std::string AMQErrorToString(amqp_rpc_reply_t reply) { - std::string errorStr; - switch (reply.reply_type) { - case AMQP_RESPONSE_NONE: errorStr = "No response"; break; - case AMQP_RESPONSE_LIBRARY_EXCEPTION: errorStr = amqp_error_string2(reply.library_error); break; - case AMQP_RESPONSE_SERVER_EXCEPTION: - if (reply.reply.id == AMQP_CONNECTION_CLOSE_METHOD) { - amqp_connection_close_t* m = (amqp_connection_close_t*) reply.reply.decoded; - errorStr = (char*)m->reply_text.bytes; - } else { - errorStr = "Unknown Error"; - } - break; - } - return fmt::format("{} ({})", errorStr, AMQReplyToString(reply.reply_type)); -} - -#define MAX_RECONNECT_ATTEMPTS 20 - -class AMQT { - private: - amqp_connection_state_t m_connection; - //std::unique_ptr m_socket; - amqp_socket_t* m_socket; - std::string client_id; - - std::atomic m_remainingAttempts = {MAX_RECONNECT_ATTEMPTS}; - std::atomic m_connected{false}; - - std::mutex m_mutex; - std::condition_variable m_cv; - - std::chrono::steady_clock::time_point m_lastActivityTime; - std::chrono::steady_clock::time_point m_lastHeartbeatTime; - - public: - AMQT(const std::string& client_id) : client_id(client_id) { - m_lastActivityTime = std::chrono::steady_clock::now(); - m_lastHeartbeatTime = m_lastActivityTime; - }; - void updateActivity() { - m_lastActivityTime = std::chrono::steady_clock::now(); - } - // since the current heartbeat does NOT work!! - bool sendHeartbeat() { - auto now = std::chrono::steady_clock::now(); - auto lastDuration = std::chrono::duration_cast(now - m_lastHeartbeatTime).count(); - // every 10 seconds - if (lastDuration >= 10) { - std::lock_guard lock(m_mutex); - if (isConnected()) { - // because WHY CANT I USE amqp_heartbeat_send!?!? we will decide to manually construct it... - amqp_frame_t frame; - frame.channel = 0; // or 1? idk... amqp_socket.c says 0 - frame.frame_type = AMQP_FRAME_HEARTBEAT; - auto res = amqp_send_frame(m_connection, &frame); - if (AMQP_STATUS_OK != res) { - log::warn("Failed to send heartbeat: {}", amqp_error_string2(res)); - return false; - } else { - //log::debug("Sent heartbeat!"); - m_lastHeartbeatTime = now; - return true; - } - } - } - return true; - } - bool isConnected() { - return m_connected.load(); - } - void setupChannel() { - //bool showPastNotifs = Mod::get()->template getSettingValue("past-notifications"); - bool showPastNotifs = false; - std::string queueName = fmt::format("amqp-queue-{}", client_id); - { - amqp_channel_open(m_connection, 1); - auto reply = amqp_get_rpc_reply(m_connection); - if (reply.reply_type != AMQP_RESPONSE_NORMAL) { - log::error("Error opening channel: {}", AMQErrorToString(reply)); - return; - } - } - // https://stackoverflow.com/questions/72237234/declare-a-queue-with-x-max-length-programmatically-using-rabbitmq-c - amqp_queue_declare_ok_t *r; - if (showPastNotifs) { - // this is absolutely cursed - /*amqp_table_entry_t q_arg_n_entries[2]; - q_arg_n_entries[0] = (amqp_table_entry_t) { - .key = amqp_cstring_bytes("x-queue-type"), - .value = {.kind = AMQP_FIELD_KIND_UTF8, .value = {.bytes = amqp_cstring_bytes("classic")}} - }; - - //86400000 - // 2 days - q_arg_n_entries[1] = (amqp_table_entry_t) { - .key = amqp_cstring_bytes("x-expires"), - .value = {.kind = AMQP_FIELD_KIND_I32, .value = {.i32 = 172800000}} - }; - amqp_table_t q_arg_table = {.num_entries = 2, .entries = q_arg_n_entries};*/ - - amqp_table_entry_t q_arg_n_entries[1]; - // 1 day - q_arg_n_entries[0] = (amqp_table_entry_t) { - .key = amqp_cstring_bytes("x-expires"), - .value = {.kind = AMQP_FIELD_KIND_I32, .value = {.i32 = 86400000}} - }; - amqp_table_t q_arg_table = {.num_entries = 1, .entries = q_arg_n_entries}; - r = amqp_queue_declare( - m_connection, 1, - amqp_cstring_bytes(queueName.c_str()), - 0, 1, 0, 0, - q_arg_table - ); - } else { - amqp_queue_delete(m_connection, 1, amqp_cstring_bytes(queueName.c_str()), 0, 0); - amqp_get_rpc_reply(m_connection); - r = amqp_queue_declare( - m_connection, 1, - amqp_cstring_bytes(queueName.c_str()), - 0, 0, 0, 1, - amqp_empty_table - ); - } - { - auto reply = amqp_get_rpc_reply(m_connection); - if (reply.reply_type != AMQP_RESPONSE_NORMAL) { - log::error("Error declaring queue: {}", AMQErrorToString(reply)); - return; - } - } - amqp_bytes_t queueBytes = amqp_bytes_malloc_dup(r->queue); - if (queueBytes.bytes == NULL) { - log::error("Couldn't copy queue name (Out of memory?)"); +#include +#include +#include +#include +#include + +bool g_socket_server_ran = false; + +$on_game(Loaded) { + if (g_socket_server_ran) return; + if (geode::Mod::get()->getSettingValue("socketServer")) { + g_socket_server_ran = true; + if (!Mod::get()->hasSavedValue("clientId")) { + // this is just for statistics ok + #ifdef GEODE_IS_WINDOWS + std::string platform = "Windows"; + #elif defined(GEODE_IS_ANDROID) + std::string platform = "Android"; + #elif defined(GEODE_IS_MACOS) + std::string platform = "Mac"; + #elif defined(GEODE_IS_IOS) + std::string platform = "iOS"; + #else + std::string platform = "Unknown"; + #endif + const std::string charset = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution<> dist(0, charset.size() - 1); + std::string randomStr; + for (size_t i = 0; i < 8; ++i) { + randomStr.push_back(charset[dist(gen)]); + } + auto now = std::chrono::system_clock::now(); + auto now_ms = std::chrono::duration_cast(now.time_since_epoch()).count(); + int accountID = 0; + if (auto gjam = GJAccountManager::get()) { + accountID = gjam->m_accountID; + } + Mod::get()->setSavedValue("clientId", fmt::format("gdutils-{}-{}-{}-{}", accountID, platform, now_ms, randomStr)); + } + if (!Mod::get()->hasSavedValue("clientId")) { + Notification::create("Couldn't connect to server, invalid Client ID", NotificationIcon::Error)->show(); return; } - { - amqp_queue_bind(m_connection, 1, - queueBytes, amqp_cstring_bytes("amq.topic"), - amqp_cstring_bytes("rate.#"), amqp_empty_table - ); - auto reply = amqp_get_rpc_reply(m_connection); - if (reply.reply_type != AMQP_RESPONSE_NORMAL) { - log::error("Error binding queue: {}", AMQErrorToString(reply)); - return; - } - } - { - auto res = amqp_basic_consume(m_connection, 1, queueBytes, amqp_empty_bytes, 0, 1, 0, amqp_empty_table); - auto reply = amqp_get_rpc_reply(m_connection); - - if (reply.reply_type != AMQP_RESPONSE_NORMAL) { - log::error("Error consuming in queue: {}", AMQErrorToString(reply)); - return; - } - if (res) { - std::string data((char*)res->consumer_tag.bytes, res->consumer_tag.len); - show_connected = true; - log::info("Subscribed to Rate Notifications! (Consumer Tag: {})", data); - } - } - - bool forceReconnection = false; - auto lastReconnectTime = std::chrono::steady_clock::now(); - - struct timeval timeout = {10, 0}; - - while (isConnected()) { - // this is just an extra so we're sure that we're always connected - if (!sendHeartbeat()) { - log::error("Couldn't send heartbeat, disconnecting..."); - break; - } - - if (forceReconnection) { - auto now = std::chrono::steady_clock::now(); - auto lastDuration = std::chrono::duration_cast(now - lastReconnectTime).count(); - if (lastDuration >= 600) { - log::info("Forcing a reconnection..."); - break; - } - } - - amqp_envelope_t envelope; - amqp_maybe_release_buffers(m_connection); - - auto ret = amqp_consume_message(m_connection, &envelope, &timeout, 0); - if (AMQP_RESPONSE_NORMAL != ret.reply_type) { - if (AMQP_RESPONSE_LIBRARY_EXCEPTION == ret.reply_type && AMQP_STATUS_HEARTBEAT_TIMEOUT == ret.library_error) { - log::error("Heartbeat failed, initiating a reconnection!"); - break; - } else if (AMQP_RESPONSE_LIBRARY_EXCEPTION == ret.reply_type && AMQP_STATUS_UNEXPECTED_STATE == ret.library_error) { - amqp_frame_t frame; - if (AMQP_STATUS_OK != amqp_simple_wait_frame(m_connection, &frame)) { - log::error("Failed to wait for frame after unexpected state: {}", AMQErrorToString(ret)); - break; - } - if (AMQP_FRAME_METHOD == frame.frame_type) { - if (frame.payload.method.id == AMQP_BASIC_ACK_METHOD) continue; - if (frame.payload.method.id == AMQP_BASIC_RETURN_METHOD) { - { - log::info("Got frame payload, re-reading message based on channel..."); - amqp_message_t message; - ret = amqp_read_message(m_connection, frame.channel, &message, 0); - if (AMQP_RESPONSE_NORMAL != ret.reply_type) { - log::error("Error reading returned message: {}", AMQErrorToString(ret)); - break; - } - std::string data((char*)message.body.bytes, message.body.len); - msgQueue.push(data); - amqp_destroy_message(&message); - } - continue; - } else { - log::error("(Frame ID: {}) Error consuming message: {}", frame.payload.method.id, AMQErrorToString(ret)); - break; - } - } else { - log::error("Response wasn't normal! {}", AMQErrorToString(ret)); - } - } else if (AMQP_RESPONSE_LIBRARY_EXCEPTION == ret.reply_type && AMQP_STATUS_TIMEOUT == ret.library_error) { - // honestly how has no one gotten this issue!? - continue; - } else { - log::error("Error consuming message: {}", AMQErrorToString(ret)); - break; - } - } else { - log::info("call rate event"); - std::string data((char*)envelope.message.body.bytes, envelope.message.body.len); - msgQueue.push(data); - amqp_destroy_envelope(&envelope); - } - } - m_connected = false; - m_cv.notify_all(); - } - void connect() { - m_remainingAttempts++; - while (m_remainingAttempts-- > 0) { - log::info("Starting AMQP..."); - if (m_remainingAttempts != MAX_RECONNECT_ATTEMPTS) { - int currentAttempt = MAX_RECONNECT_ATTEMPTS - m_remainingAttempts + 1; - int delayMs = 3000 * std::pow(2, currentAttempt - 1); - //TODO: fix it being stuck on 3 seconds - log::warn("Disconnected from server. Attempting to reconnect... ({} left, reconnecting in {} seconds)", m_remainingAttempts, (float)((delayMs / 1000.F))); - std::this_thread::sleep_for(std::chrono::milliseconds(delayMs)); - } - m_connection = amqp_new_connection(); - amqp_socket_t* socket = amqp_tcp_socket_new(m_connection); - if (!socket) { - log::error("Couldn't create TCP connection"); - continue; - } - int status; - status = amqp_socket_open(socket, "gdutils.clarifygdps.com", 5672); - if (status != AMQP_STATUS_OK) { - log::error("Error opening socket! {}", amqp_error_string2(status)); - continue; - } - if (socket && m_connection) { - // heartbeat every 10 seconds! - amqp_rpc_reply_t reply = amqp_login(m_connection, "/", 0, 131072, 10, AMQP_SASL_METHOD_PLAIN, "gd", "GeometryDashisahorizontalrunnerstylegamedevelopedandpublishedbyRobTopGames"); - if (reply.reply_type != AMQP_RESPONSE_NORMAL) { - const char* errorStr = nullptr; - log::error("Couldn't login to AMQP server! {}", AMQErrorToString(reply)); - continue; - } - m_connected = true; - setupChannel(); - /* - std::unique_lock lock(m_mutex); - if (!m_cv.wait_for(lock, std::chrono::seconds(10), [this]{ - return m_connection; // change thispls - })) { - log::error("Connection timeout, assuming I can't connect!"); - } - while (isConnected()) { - m_cv.wait_for(lock, std::chrono::seconds(1), [this] { return !isConnected(); }); - }*/ - //amqp_connection_close(m_connection, AMQP_REPLY_SUCCESS); - disconnect(); - } - } - log::error("Maximum reconnection attempts reached, cannot reconnect. Restart the game to reconnect to the MQTT server."); - } - void disconnect() { - if (m_connection) { - m_connected = false; - amqp_destroy_connection(m_connection); - m_connection = nullptr; - } - } -}; - -static std::unordered_map RUNNING_REQUESTS {}; -static std::mutex lock_var; - -std::string xorEncrypt(const std::string& input, const std::string& key) { - std::string result; - for (size_t i = 0; i < input.size(); ++i) { - result += input[i] ^ key[i % key.size()]; - } - return result; -} - -// Daily chests notifications -/*void dailyChestThread() { - while (true) { - chestQueue.push(1); - std::this_thread::sleep_for(std::chrono::minutes(1)); - } -}*/ - -void processEvent(CCScene* self) { - if (!dataQueue.empty()) { - auto data = dataQueue.front(); - dataQueue.pop(); - EventsPush::pushRateLevel(self, data); - } -} - -void processChestEvent(CCScene* self) { - if (!chestQueue.empty()) { - auto data = chestQueue.front(); - chestQueue.pop(); - auto dailyChest = new DailyChest(); - dailyChest->getRewards(0); - } -} - -class EventHandler : public CCObject { - protected: - std::string m_currentLayer; - public: - static EventHandler* create() { - auto pRet = new EventHandler(); - if (pRet) { - pRet->autorelease(); - return pRet; - } - CC_SAFE_DELETE(pRet); - return nullptr; - } - void checkForEvent() { - auto director = CCDirector::sharedDirector(); - if (!director) return; - bool waitUntilExit = false; - auto scene = CCDirector::sharedDirector()->getRunningScene(); - if (!scene) return; - if (scene->getChildrenCount() == 0) return; - if (show_connected) { - show_connected = false; - if (Mod::get()->template getSettingValue("server-notification")) { - Notification::create("Connected to Rate Server!", NotificationIcon::Success)->show(); - } - } - auto layer = scene->getChildren()->objectAtIndex(0); - if (ProcessLambdas::shouldProcessMenuHandler()) { - ProcessLambdas::processMenuHandler(); - } - std::string layerName = misc::getNodeName(layer); - if (layerName == "cocos2d::CCLayerColor") return; - if (layerName == "LoadingLayer") return; - if (m_currentLayer != layerName) { - m_currentLayer = layerName; - EventsPush::stopNow(scene); - bool everywhereElse = Mod::get()->template getSettingValue("everywhereElse"); - if ((layerName != "LevelEditorLayer" && layerName != "PlayLayer") && !everywhereElse) return; - if ((layerName != "LevelEditorLayer" && layerName != "PlayLayer") && everywhereElse) { - processChestEvent(scene); - processEvent(scene); - } - } - if (msgQueue.empty()) return; - bool everywhereElse = Mod::get()->template getSettingValue("everywhereElse"); - bool inLevels = Mod::get()->template getSettingValue("inLevels"); - bool inEditor = Mod::get()->template getSettingValue("inEditor"); - bool inPlatformers = Mod::get()->template getSettingValue("inPlatformers"); - - bool pushEvent = true; - if (layerName == "PlayLayer") { - if (!inLevels) pushEvent = false; - - PlayLayer* playLayer = reinterpret_cast(layer); - GJGameLevel* level = playLayer->m_level; - if (level->isPlatformer() && !inPlatformers) { - pushEvent = false; - } - } - if (layerName == "LevelEditorLayer" && !inEditor) { - pushEvent = false; - } - if ((layerName != "LevelEditorLayer" && layerName != "PlayLayer") && !everywhereElse) { - pushEvent = false; - } - dataQueue.push(msgQueue.front()); - msgQueue.pop(); - if (pushEvent) processEvent(scene); - } - void checkForFiles() { - #ifdef GEODE_IS_WINDOWS - auto scene = CCDirector::sharedDirector()->getRunningScene(); - if (!scene) return; - if (scene->getChildrenCount() == 0) return; - auto layer = scene->getChildren()->objectAtIndex(0); - if (layer == nullptr) return; - std::string layerName = misc::getNodeName(layer); - if (layerName == "cocos2d::CCLayerColor") return; - if (layerName == "LoadingLayer") return; - if (layerName == "LevelEditorLayer") return; - if (layerName == "PlayLayer") return; - if (layerName == "LoadingLayer") return; - bool levelsProtocol = Mod::get()->template getSettingValue("levelsProtocol"); - if (!levelsProtocol) return; - auto glm = GameLevelManager::sharedState(); - try { - for (const auto & entry : std::filesystem::directory_iterator("gdutils")) { - if (entry.path().extension() == ".flag") { - std::string path = entry.path().string(); - std::string filename = entry.path().filename().string(); - std::string levelName = filename.substr(0, filename.length() - 5); - - try { - std::filesystem::remove("gdutils/" + filename); - } catch (const std::exception& e) { - break; - } - - std::string url = "https://www.boomlings.com/database/getGJLevels21.php"; - std::string fields = fmt::format("secret=Wmfd2893gb7&type=0&str={}", levelName); - - geode::utils::web::WebRequest request = web::WebRequest(); - request.userAgent(""); - request.header("Content-Type", "application/x-www-form-urlencoded"); - const std::lock_guard lock(lock_var); - RUNNING_REQUESTS.emplace( - "@loaderLevelProtocolURL", - request.bodyString(fields).post(url).map( - [](web::WebResponse* response) { - const std::lock_guard lock(lock_var); - if (response->ok()) { - if (response->data().empty()) { - FLAlertLayer::create(nullptr, - "Error (GDUtils)", - "An error has occured with Robtop's servers. The response returned empty.", - "OK", - nullptr, - 180.0F - )->show(); - } else { - auto data = response->string().unwrapOrDefault(); - if (data != "-1") { - auto scene = CCScene::create(); - - GJGameLevel* gjgl = EventsPush::convertLevelToJSON(data); - - auto layer = LevelInfoLayer::create(gjgl, false); - layer->downloadLevel(); - scene->addChild(layer); - CCDirector::sharedDirector()->pushScene(cocos2d::CCTransitionFade::create(0.5f, scene)); - } else { - FLAlertLayer::create(nullptr, - "Error", - "Level not found.", - "OK", - nullptr, - 180.0F - )->show(); - } - } - } else { - FLAlertLayer::create(nullptr, - "Error (GDUtils)", - "An error has occured with Robtop's servers. The reason could be because you are either ratelimited, blocked, or another issue.", - "OK", - nullptr, - 180.0F - )->show(); - } - - RUNNING_REQUESTS.erase("@loaderLevelProtocolURL"); - return *response; + auto [tx, rx] = arc::mpsc::channel(1024); // i mean why would robtop ever rate that many levels + auto [etx, erx] = arc::mpsc::channel(1024); // ditto + auto [ctx, crx] = arc::mpsc::channel(64); + AMQT::setTx(std::move(tx)); + EventsPush::setTx(std::move(ctx)); + async::runtime().spawn([rx = std::move(rx), crx = std::move(crx), erx = std::move(erx)]() mutable -> arc::Future<> { + bool running = true; + while (running) { + co_await arc::select( + arc::selectee(rx.recv(), [&](auto req) { + if (req) { + std::string msg = std::move(req).unwrap(); + Loader::get()->queueInMainThread([m = std::move(msg)]() mutable { + auto director = CCDirector::sharedDirector(); + if (!director) return; + auto scene = CCDirector::sharedDirector()->getRunningScene(); + if (!scene) return; + if (scene->getChildrenCount() == 0) return; + if (EventsPush::canSendEvent(scene)) { + EventsPush::pushRateLevel(scene, m); } - ) - ); - } - } - } catch (const std::exception& e) {} - #endif - } - void check(float dt) { - checkForEvent(); - checkForFiles(); - } -}; - -// Backgrounds -// i dont like ios -#ifndef GEODE_IS_IOS -class $modify(UtilCCSprite, CCSprite) { - static CCSprite* create(char const* name) { - auto ret = CCSprite::create(name); - if (ret == nullptr) return ret; - // better code, thanks camila! - if (!strcmp(name, "GJ_gradientBG.png") && Mod::get()->template getSettingValue("activate-background")) { - ret->retain(); - Loader::get()->queueInMainThread([=] { - if (ret != nullptr) { - if (ret->getColor() == ccc3(0, 102, 255)) { - auto scene = CCDirector::sharedDirector()->getRunningScene(); - if (scene->getChildrenCount() == 0) return; - auto layer = scene->getChildren()->objectAtIndex(0); - std::string layerName = misc::getNodeName(layer); - ret->setColor(Mod::get()->template getSettingValue("background")); - } - ret->release(); - } - }); - - } - return ret; - } -}; - -// Child background -class $modify(UtilCCScale9, cocos2d::extension::CCScale9Sprite) { - static cocos2d::extension::CCScale9Sprite* create(char const* name, CCRect rect) { - auto ret = cocos2d::extension::CCScale9Sprite::create(name, rect); - if (ret == nullptr) return ret; - // better code, thanks camila! - if ((!strcmp(name, "square02b_001.png") || !strcmp(name, "square02b_small.png")) && Mod::get()->template getSettingValue("activate-background")) { - ret->retain(); - Loader::get()->queueInMainThread([=] { - if (ret != nullptr) { - // jouca waht - if (ret->getColor() == ccc3(0, 56, 141)) { - ccColor3B new_color = Mod::get()->template getSettingValue("child-background"); - if (Mod::get()->template getSettingValue("syncBGcolor")) { - new_color = Mod::get()->template getSettingValue("background"); - } - if (Mod::get()->template getSettingValue("addChildBDGradient")) { - new_color = ccc3(new_color.r * 0.79, new_color.g * 0.79, new_color.b * 0.79); - } - if (Mod::get()->template getSettingValue("bgchild-transparency")) { - int opacity = Mod::get()->template getSettingValue("bgchild-transparency-scroll"); - ret->setOpacity(opacity); - } - ret->setColor(new_color); - } else if (ret->getColor() == ccc3(0, 39, 98)) { - ccColor3B new_color = Mod::get()->template getSettingValue("child-background"); - if (Mod::get()->template getSettingValue("syncBGcolor")) { - new_color = Mod::get()->template getSettingValue("background"); - } - if (Mod::get()->template getSettingValue("addChildBDGradient")) { - new_color = ccc3(new_color.r * 0.71, new_color.g * 0.71, new_color.b * 0.71); - } - if (Mod::get()->template getSettingValue("bgchild-transparency")) { - int opacity = Mod::get()->template getSettingValue("bgchild-transparency-scroll"); - ret->setOpacity(opacity); - } - ret->setColor(new_color); - } else if (ret->getColor() == ccc3(0, 46, 117)) { - ccColor3B new_color = Mod::get()->template getSettingValue("child-background"); - if (Mod::get()->template getSettingValue("syncBGcolor")) { - new_color = Mod::get()->template getSettingValue("background"); - } - if (Mod::get()->template getSettingValue("addChildBDGradient")) { - new_color = ccc3(new_color.r * 0.74, new_color.g * 0.74, new_color.b * 0.74); - } - if (Mod::get()->template getSettingValue("bgchild-transparency")) { - int opacity = Mod::get()->template getSettingValue("bgchild-transparency-scroll"); - ret->setOpacity(opacity); - } - ret->setColor(new_color); - } else if (ret->getColor() == ccc3(0, 36, 91)) { - ccColor3B new_color = Mod::get()->template getSettingValue("child-background"); - if (Mod::get()->template getSettingValue("syncBGcolor")) { - new_color = Mod::get()->template getSettingValue("background"); - } - if (Mod::get()->template getSettingValue("addChildBDGradient")) { - new_color = ccc3(new_color.r * 0.70, new_color.g * 0.70, new_color.b * 0.70); - } - if (Mod::get()->template getSettingValue("bgchild-transparency")) { - int opacity = Mod::get()->template getSettingValue("bgchild-transparency-scroll"); - ret->setOpacity(opacity); - } - ret->setColor(new_color); - } else if (ret->getColor() == ccc3(0, 31, 79)) { - ccColor3B new_color = Mod::get()->template getSettingValue("child-background"); - if (Mod::get()->template getSettingValue("syncBGcolor")) { - new_color = Mod::get()->template getSettingValue("background"); - } - if (Mod::get()->template getSettingValue("addChildBDGradient")) { - new_color = ccc3(new_color.r * 0.67, new_color.g * 0.67, new_color.b * 0.67); + }); } - if (Mod::get()->template getSettingValue("bgchild-transparency")) { - int opacity = Mod::get()->template getSettingValue("bgchild-transparency-scroll"); - ret->setOpacity(opacity); + }), + arc::selectee(crx.recv(), [&](auto req) { + if (req) { + bool b = std::move(req).unwrap(); + log::debug("Cancelled (Code = {})", b); + } else { + log::debug("Cancelled (Code = Unknown)"); } - ret->setColor(new_color); - } - ret->release(); - } - }); - } - return ret; - } -}; -#endif - -//bool is_dailychest_ready = false; -bool is_socketserver_started = false; -class $modify(MenuLayer) { - bool init() { - if (!MenuLayer::init()) return false; - - /*if (!is_dailychest_ready) { - std::thread hThread(dailyChestThread); - hThread.detach(); - is_dailychest_ready = true; - }*/ - if (!is_socketserver_started) { - bool startSocketServer = Mod::get()->template getSettingValue("socketServer"); - if (startSocketServer) { - if (!Mod::get()->hasSavedValue("clientId")) { - Mod::get()->setSavedValue("clientId", misc::genClientID()); - } - if (!Mod::get()->hasSavedValue("clientId")) { - Notification::create("Couldn't connect to server, invalid Client ID", NotificationIcon::Error)->show(); - return true; - } - std::thread hThread([]() { - AMQT handler(Mod::get()->getSavedValue("clientId")); - handler.connect(); - }); - hThread.detach(); - } - is_socketserver_started = true; - } - return true; - } -}; - -void toggleSpriteHooks(bool value) { - for (auto& hook : geode::Mod::get()->getHooks()) { - if (hook->getDisplayName() == "cocos2d::CCSprite::create" || hook->getDisplayName() == "cocos2d::extension::CCScale9Sprite::create") { - if (value) { - (void)hook->enable(); - } else { - (void)hook->disable(); - } - } - } -} - -$execute { - (void)Mod::get()->registerCustomSettingType("credit-buttons", &SettingCreditsValue::parse); - //(void)Mod::get()->registerCustomSettingType("notificationPlacement", &SettingPosValue::parse); - auto ret = Mod::get()->registerCustomSettingType("notification-placement", &SettingPosValue::parse); - if (!ret) { - log::error("Unable to register setting type: {}", ret.unwrapErr()); - } - Loader::get()->queueInMainThread([]{ - CCScheduler::get()->scheduleSelector(schedule_selector(EventHandler::check), EventHandler::create(), 1.0F, false); - //CCScheduler::get()->scheduleUpdateForTarget(EventHandler::create(), Priority::Last, false); - }); - listenForSettingChanges("activate-background", [](bool value) { - log::info("Activate Background changed to {}", value); - toggleSpriteHooks(value); - }); -} - -// When the socket connection is made -$on_mod(Loaded) { - log::info("GDUtils Mod Loaded"); - - if (!Mod::get()->getSettingValue("activate-background")) { - toggleSpriteHooks(false); - } - chestQueue.push(1); - - //Discord::init(); for next update ;) - // also for whatever reason, discord rpc doesnt work on vanilla gd, instead it shows absolutes MH icon for some reason even though I dont own MH. can someone explain that - - #ifdef GEODE_IS_WINDOWS - #include - #include - - std::array buffer; - GetModuleFileNameW(NULL, buffer.data(), MAX_PATH); - auto aaaa = buffer.data(); - std::wstring_convert> converter; - std::string nameStr = fmt::format("cmd /k cd \"{}\\gdutils\" && \"gd_protocol.bat\" %1 && exit", dirs::getGameDir()); - - std::wstring fullPath = std::wstring(buffer.data()); - size_t lastBackslashPos = fullPath.find_last_of(L"\\"); - std::wstring exeName; - if (lastBackslashPos != std::wstring::npos) { - exeName = fullPath.substr(lastBackslashPos + 1); - } - - // Create the key for the custom protocol in user registry - HKEY hCurrentUserKey; - if (RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\Classes", 0, KEY_SET_VALUE, &hCurrentUserKey) == ERROR_SUCCESS) { - HKEY hKey; - if (RegCreateKeyEx(hCurrentUserKey, "gdutils", 0, NULL, 0, KEY_SET_VALUE, NULL, &hKey, NULL) == ERROR_SUCCESS) { - // Define the default value for the protocol key - if (RegSetValueEx(hKey, NULL, 0, REG_SZ, (BYTE*)"URL:gdutils Protocol", sizeof("URL:gdutils Protocol")) == ERROR_SUCCESS) { - // Create the key "shell\open\command" for specify the exe path - HKEY hCommandKey; - if (RegCreateKeyEx(hKey, "shell\\open\\command", 0, NULL, 0, KEY_SET_VALUE, NULL, &hCommandKey, NULL) == ERROR_SUCCESS) { - if (RegSetValueExA(hCommandKey, NULL, 0, REG_SZ, (const BYTE*)nameStr.c_str(), nameStr.length() + 1) == ERROR_SUCCESS) { - log::info("Le protocole a été enregistré avec succès pour cet utilisateur."); - } else { - log::error("Erreur lors de la définition de la valeur du chemin de l'exécutable."); - } - RegCloseKey(hCommandKey); - } else { - log::error("Erreur lors de la création de la clé shell\\open\\command."); - } - - RegSetValueEx(hKey, "URL Protocol", 0, REG_SZ, NULL, NULL); - } else { - log::error("Erreur lors de la définition de la valeur par défaut du protocole."); + Loader::get()->queueInMainThread([]() { + if (auto scene = CCScene::get()) { + EventsPush::eventCompletedCallback(scene); + } + }); + }) + ); } - RegCloseKey(hKey); - } else { - log::error("Erreur lors de la création de la clé du protocole."); - } - RegCloseKey(hCurrentUserKey); - } else { - log::error("Erreur lors de l'ouverture de la clé du registre utilisateur."); + }); + std::thread hThread([]() mutable { + AMQT handler(Mod::get()->getSavedValue("clientId")); + handler.connect(); + }); + hThread.detach(); } - - std::filesystem::create_directory("gdutils"); - if (!std::filesystem::exists("gdutils/gd_protocol.bat")) { - std::ofstream file("gdutils/gd_protocol.bat"); - file << R"( -@echo off -setlocal enabledelayedexpansion - - )"; - file << fmt::format("set \"appName={}\"", converter.to_bytes(exeName)); - file << R"( -set "argument=%1" -set "number=" - -REM Check if the argument contains / -echo !argument! | find "/" > nul -if not errorlevel 1 ( - REM If / is found, extract the number part after the / - for /f "tokens=2 delims=/" %%a in ("!argument!") do ( - set "number=%%a" - ) -) else ( - REM If / is not found, the argument is in the format gdutils:123 - for /f "tokens=2 delims=:" %%a in ("!argument!") do ( - set "number=%%a" - ) -) - -if not defined number ( - echo Invalid argument format: %1 - exit /b 1 -) - -set "flagFile=%number%.flag" - -REM Check if the application is already running -tasklist /FI "IMAGENAME eq %appName%" | find /i "%appName%" > nul -if errorlevel 1 ( - REM If not running, start the application with the provided argument - cd .. - start "" "%appName%" - cd gdutils - type nul > "%flagFile%" -) else ( - REM If running, create an empty file based on the extracted number - type nul > "%flagFile%" -) - -endlocal - )"; - file.close(); - } - - #endif } diff --git a/src/modules/QueueHandler.cpp b/src/modules/QueueHandler.cpp new file mode 100644 index 0000000..8708d1e --- /dev/null +++ b/src/modules/QueueHandler.cpp @@ -0,0 +1,10 @@ +#include "QueueHandler.hpp" + +std::queue rateQueue; + +std::queue QueueHandler::getRateQueue() { + return rateQueue; +}; +void QueueHandler::addToRate(std::string value) { + rateQueue.push(value); +} diff --git a/src/modules/QueueHandler.hpp b/src/modules/QueueHandler.hpp new file mode 100644 index 0000000..d6486dc --- /dev/null +++ b/src/modules/QueueHandler.hpp @@ -0,0 +1,7 @@ +#pragma once +#include + +namespace QueueHandler { + std::queue getRateQueue(); + void addToRate(std::string); +} diff --git a/src/Utils/SpotifyHandler.cpp b/src/modules/SpotifyHandler.cpp similarity index 87% rename from src/Utils/SpotifyHandler.cpp rename to src/modules/SpotifyHandler.cpp index a2265fa..444cca8 100644 --- a/src/Utils/SpotifyHandler.cpp +++ b/src/modules/SpotifyHandler.cpp @@ -1,16 +1,15 @@ // Spotify +// sorry this code will forever be bad bool is_muted = false; -#include "../includes.h" +#include #include #include #include -#include "../Settings/CustomSettings.hpp" #ifdef GEODE_IS_WINDOWS #include #include #include #include -#include #include #include #include @@ -230,18 +229,13 @@ void toggleSpotifyMute(bool automatic = false, bool muted = false) { class $modify(PlayLayer) { bool init(GJGameLevel* level, bool p1, bool p2) { if (!PlayLayer::init(level, p1, p2)) return false; - if (!Mod::get()->getSettingValue("inLevelsSpotify")) return true; - //auto gm = FMODAudioEngine::sharedEngine(); - //if (gm->m_backgroundMusicVolume > 0.0f) { - // uncomment when fmod gets these fields - if (1.0F > 0.0F) { + if (FMODAudioEngine::sharedEngine()->m_musicVolume > 0.0F) { toggleSpotifyMute(true, true); } return true; } void onQuit() { PlayLayer::onQuit(); - if (!Mod::get()->getSettingValue("inLevelsSpotify")) return; if (is_muted) { toggleSpotifyMute(true, false); } @@ -254,8 +248,7 @@ class $modify(PauseLayer) { void goEdit() { PauseLayer::goEdit(); if (!Mod::get()->getSettingValue("inLevelsSpotify")) return; - //if (gm->m_backgroundMusicVolume > 0.0f) { - if (1 > 0.0F) { // temp until someone adds fields + if (FMODAudioEngine::sharedEngine()->m_musicVolume > 0.0F) { // temp until someone adds fields toggleSpotifyMute(); } } @@ -264,45 +257,66 @@ class $modify(PauseLayer) { class $modify(EditorUI) { void onPlaytest(CCObject* sender) { EditorUI::onPlaytest(sender); - if (!Mod::get()->getSettingValue("inEditorSpotify")) return; - //if (gm->m_backgroundMusicVolume > 0.0f) { - if (1 > 0.0F) { // temp until someone adds fields + if (FMODAudioEngine::sharedEngine()->m_musicVolume > 0.0F) { // temp until someone adds fields toggleSpotifyMute(); } } void onStopPlaytest(CCObject* sender) { EditorUI::onStopPlaytest(sender); - if (!Mod::get()->getSettingValue("inEditorSpotify")) return; - if (is_muted) { toggleSpotifyMute(true, false); } } void playtestStopped() { EditorUI::playtestStopped(); - if (!Mod::get()->getSettingValue("inEditorSpotify")) return; - if (is_muted) { toggleSpotifyMute(true, false); } } void onPlayback(CCObject* sender) { EditorUI::onPlayback(sender); - if (!Mod::get()->getSettingValue("inEditorSpotify")) return; - - //if (gm->m_backgroundMusicVolume > 0.0f) { - if (1 > 0.0F) { // temp until someone adds fields + if (FMODAudioEngine::sharedEngine()->m_musicVolume > 0.0F) { // temp until someone adds fields toggleSpotifyMute(); } } void onPause(CCObject* sender) { if (is_muted) { - if (Mod::get()->getSettingValue("inEditorSpotify")) { - toggleSpotifyMute(true, false); - } + toggleSpotifyMute(true, false); } EditorUI::onPause(sender); } }; #endif // Spotify + +void toggleHook(bool level, bool value) { + for (auto& hook : geode::Mod::get()->getHooks()) { + if (level && !hook->getDisplayName().starts_with("PauseLayer") && !hook->getDisplayName().starts_with("PlayLayer")) continue; + if (!level && !hook->getDisplayName().starts_with("EditorUI")) continue; + if (value) { + (void)hook->enable(); + } else { + (void)hook->disable(); + } + } +} + +$execute { + listenForSettingChanges("inLevelsSpotify", [](bool value) { + log::info("Spotify In Level changed to {}", value); + toggleHook(true, value); + }); + listenForSettingChanges("inEditorSpotify", [](bool value) { + log::info("Spotify In Editor changed to {}", value); + toggleHook(false, value); + }); +} + +$on_mod(Loaded) { + if (!Mod::get()->getSettingValue("inLevelsSpotify")) { + toggleHook(true, false); + } + if (!Mod::get()->getSettingValue("inEditorSpotify")) { + toggleHook(false, false); + } +} diff --git a/src/modules/events/EventHandler.cpp b/src/modules/events/EventHandler.cpp new file mode 100644 index 0000000..cc6207a --- /dev/null +++ b/src/modules/events/EventHandler.cpp @@ -0,0 +1,94 @@ +#include "EventHandler.hpp" +#include "EventsPush.hpp" +#include + +std::string getNodeNameA(cocos2d::CCObject* node) { +#ifdef GEODE_IS_WINDOWS + return typeid(*node).name() + 6; +#else + { + std::string ret; + + int status = 0; + auto demangle = abi::__cxa_demangle(typeid(*node).name(), 0, 0, &status); + if (status == 0) { + ret = demangle; + } + free(demangle); + + return ret; + } +#endif +} + +void processEvent(CCScene* self) { + if (!AMQT::getQueue().empty()) { + EventsPush::pushRateLevel(self, AMQT::getQueueFrontPop()); + } +} + +//TODO: +void processChestEvent(CCScene* self) { + /*if (!chestQueue.empty()) { + auto data = chestQueue.front(); + chestQueue.pop(); + auto dailyChest = new DailyChest(); + dailyChest->getRewards(0); + }*/ +} + + +void EventHandler::checkForEvent() { + auto director = CCDirector::sharedDirector(); + if (!director) return; + auto scene = CCDirector::sharedDirector()->getRunningScene(); + if (!scene) return; + if (scene->getChildrenCount() == 0) return; + if (AMQT::genConnected()) { + AMQT::setGenConnected(false); + if (Mod::get()->template getSettingValue("server-notification")) { + Notification::create("Connected to Rate Server!", NotificationIcon::Success)->show(); + } + } + auto layer = scene->getChildren()->objectAtIndex(0); + std::string layerName = getNodeNameA(layer); + if (layerName == "cocos2d::CCLayerColor") return; + if (layerName == "LoadingLayer") return; + if (m_currentLayer != layerName) { + m_currentLayer = layerName; + EventsPush::eventCompletedCallback(scene); + bool everywhereElse = Mod::get()->template getSettingValue("everywhereElse"); + if ((layerName != "LevelEditorLayer" && layerName != "PlayLayer") && !everywhereElse) return; + if ((layerName != "LevelEditorLayer" && layerName != "PlayLayer") && everywhereElse) { + processChestEvent(scene); + processEvent(scene); + } + } + if (AMQT::getQueue().empty()) return; + bool everywhereElse = Mod::get()->getSettingValue("everywhereElse"); + bool inLevels = Mod::get()->getSettingValue("inLevels"); + bool inEditor = Mod::get()->getSettingValue("inEditor"); + bool inPlatformers = Mod::get()->getSettingValue("inPlatformers"); + bool pushEvent = true; + if (layerName == "PlayLayer") { + if (!inLevels) pushEvent = false; + } + if (layerName == "LevelEditorLayer" && !inEditor) { + pushEvent = false; + } + if ((layerName != "LevelEditorLayer" && layerName != "PlayLayer") && !everywhereElse) { + pushEvent = false; + } + if (pushEvent) processEvent(scene); +} + +void EventHandler::consumer(arc::mpsc::Receiver rx, CCScene* scene) { + +} + +$execute { + /*Loader::get()->queueInMainThread([]{ + CCScheduler::get()->scheduleSelector(schedule_selector(EventHandler::check), EventHandler::create(), 1.0F, false); + //CCScheduler::get()->scheduleUpdateForTarget(EventHandler::create(), Priority::Last, false); + });*/ +} diff --git a/src/modules/events/EventHandler.hpp b/src/modules/events/EventHandler.hpp new file mode 100644 index 0000000..a2775c1 --- /dev/null +++ b/src/modules/events/EventHandler.hpp @@ -0,0 +1,119 @@ +#pragma once +#include +#include + +class EventHandler : public CCObject { + protected: + std::string m_currentLayer; + public: + static EventHandler* create() { + auto pRet = new EventHandler(); + if (pRet) { + pRet->autorelease(); + return pRet; + } + CC_SAFE_DELETE(pRet); + return nullptr; + } + void checkForEvent(); + static void aaa(); +#if 0 + void checkForFiles() { + #ifdef GEODE_IS_WINDOWS + auto scene = CCDirector::sharedDirector()->getRunningScene(); + if (!scene) return; + if (scene->getChildrenCount() == 0) return; + auto layer = scene->getChildren()->objectAtIndex(0); + if (layer == nullptr) return; + std::string layerName = misc::getNodeName(layer); + if (layerName == "cocos2d::CCLayerColor") return; + if (layerName == "LoadingLayer") return; + if (layerName == "LevelEditorLayer") return; + if (layerName == "PlayLayer") return; + if (layerName == "LoadingLayer") return; + bool levelsProtocol = Mod::get()->template getSettingValue("levelsProtocol"); + if (!levelsProtocol) return; + auto glm = GameLevelManager::sharedState(); + try { + for (const auto & entry : std::filesystem::directory_iterator("gdutils")) { + if (entry.path().extension() == ".flag") { + std::string path = entry.path().string(); + std::string filename = entry.path().filename().string(); + std::string levelName = filename.substr(0, filename.length() - 5); + + try { + std::filesystem::remove("gdutils/" + filename); + } catch (const std::exception& e) { + break; + } + + std::string url = "https://www.boomlings.com/database/getGJLevels21.php"; + std::string fields = fmt::format("secret=Wmfd2893gb7&type=0&str={}", levelName); + + geode::utils::web::WebRequest request = web::WebRequest(); + request.userAgent(""); + request.header("Content-Type", "application/x-www-form-urlencoded"); + const std::lock_guard lock(lock_var); + RUNNING_REQUESTS.emplace( + "@loaderLevelProtocolURL", + request.bodyString(fields).post(url).map( + [](web::WebResponse* response) { + const std::lock_guard lock(lock_var); + if (response->ok()) { + if (response->data().empty()) { + FLAlertLayer::create(nullptr, + "Error (GDUtils)", + "An error has occured with Robtop's servers. The response returned empty.", + "OK", + nullptr, + 180.0F + )->show(); + } else { + auto data = response->string().unwrapOrDefault(); + if (data != "-1") { + auto scene = CCScene::create(); + + GJGameLevel* gjgl = EventsPush::convertLevelToJSON(data); + + auto layer = LevelInfoLayer::create(gjgl, false); + layer->downloadLevel(); + scene->addChild(layer); + CCDirector::sharedDirector()->pushScene(cocos2d::CCTransitionFade::create(0.5f, scene)); + } else { + FLAlertLayer::create(nullptr, + "Error", + "Level not found.", + "OK", + nullptr, + 180.0F + )->show(); + } + } + } else { + FLAlertLayer::create(nullptr, + "Error (GDUtils)", + "An error has occured with Robtop's servers. The reason could be because you are either ratelimited, blocked, or another issue.", + "OK", + nullptr, + 180.0F + )->show(); + } + + RUNNING_REQUESTS.erase("@loaderLevelProtocolURL"); + return *response; + } + ) + ); + } + } + } catch (const std::exception& e) {} + #endif + } +#endif + void check(float dt) { + checkForEvent(); + //checkForFiles(); + } + static void consumer(arc::mpsc::Receiver rx, CCScene* scene); +}; + diff --git a/src/modules/events/EventsPush.cpp b/src/modules/events/EventsPush.cpp new file mode 100644 index 0000000..dc81ebd --- /dev/null +++ b/src/modules/events/EventsPush.cpp @@ -0,0 +1,444 @@ +#include "EventsPush.hpp" +#include +#include +#include +#include + +std::queue g_eventQueue; +bool g_processingEvents = false; +std::optional> g_finishTx; +//extern std::optional> s_tx; + +const char* getDemonDifficultyIcon(int stars) { + switch(stars){ + case 3: + return "difficulty_07_btn_001.png"; + case 4: + return "difficulty_08_btn_001.png"; + case 5: + return "difficulty_09_btn_001.png"; + case 6: + return "difficulty_10_btn_001.png"; + default: + return "difficulty_06_btn_001.png"; + } +} +const char* getDifficultyIcon(int stars) { + switch(stars){ + case 10: + return "difficulty_01_btn_001.png"; + case 20: + return "difficulty_02_btn_001.png"; + case 30: + return "difficulty_03_btn_001.png"; + case 40: + return "difficulty_04_btn_001.png"; + case 50: + return "difficulty_05_btn_001.png"; + case 60: + return "difficulty_auto_btn_001.png"; + default: + return "difficulty_00_btn_001.png"; + } +} +// stole from GDR mod lol +GJDifficulty getDifficulty(int stars) { + switch(stars){ + case 0: + return GJDifficulty::NA; + case 10: + return GJDifficulty::Easy; + case 20: + return GJDifficulty::Normal; + case 30: + return GJDifficulty::Hard; + case 40: + return GJDifficulty::Harder; + case 50: + return GJDifficulty::Insane; + case 60: + return GJDifficulty::Auto; + default: + return GJDifficulty::NA; + } +} +GJDifficulty getDemonDifficulty(int stars) { + switch(stars){ + case 3: + return GJDifficulty::DemonEasy; + case 4: + return GJDifficulty::DemonMedium; + case 5: + return GJDifficulty::DemonInsane; + case 6: + return GJDifficulty::DemonExtreme; + default: + return GJDifficulty::Demon; + } +} + +bool EventsPush::init(EventData data) { + if (!CCLayer::init()) return false; + auto scene = CCDirector::sharedDirector()->getRunningScene(); + auto director = CCDirector::sharedDirector(); + auto winSize = director->getWinSize(); + // type + // 0 = new rate + // 1 = daily + // 2 = weekly + + // rate + // 0 = unrated / star rate + // 1 = featured + // 2 = epic + // {'demon': 0, 'type': 0, 'starsum': 20, 'stars': 2, 'rate': 2, 'title': 'New Rated Level !', 'level_name': 'Pourquoi', 'level_creator': 'by Jouca', 'sprite': 'GJ_square01.png'} + int starsum = data.starsum; + int stars = data.stars; + int coins = data.coins; + int areCoinsVerified = data.verified_coins; + std::unordered_map sKeys { + { EventType::Rate, "newRate" }, + { EventType::Daily, "daily" }, + { EventType::Weekly, "weekly" }, + { EventType::smallChest, "event" }, + { EventType::largeChest, "smallChest" }, + { EventType::List, "largeChest" }, + { EventType::Event, "newListRate" }, + { EventType::Announcement, "announcement" }, + }; + eventType = data.type; + auto it = sKeys.find(data.type); + if (it != sKeys.end()) { + if (!Mod::get()->getSettingValue(it->second)) { + EventsPush::eventCompletedCallback(scene); + return true; + } + } + float lrScale = (float)Mod::get()->getSettingValue("size"); + + auto node = Build::create().anchorPoint(0.5, 0.5).pos(77, 30).collect(); + auto bg = Build::create(data.sprite.c_str()).contentSize(240, 70).scale(lrScale).child(node).parent(this).collect(); + float moveX = 0.f; + switch (Mod::get()->getSettingValue("notificationPlacement")) { + case PositionEnum::TopLeft: // top left + bg->setPosition((-(bg->getContentSize().width / 2)) * lrScale, ((winSize.height - (bg->getContentSize().height / 2)) - (20 * lrScale))); + moveX = (bg->getContentSize().width) * lrScale; + break; + case PositionEnum::TopRight: // top right + bg->setPosition(winSize.width + ((bg->getContentSize().width / 2) * lrScale), ((winSize.height - (bg->getContentSize().height / 2)) - (20 * lrScale))); + moveX = -(bg->getContentSize().width * lrScale); + break; + case PositionEnum::BottomLeft: // bottom left + bg->setPosition((-(bg->getContentSize().width / 2)) * lrScale, (bg->getContentSize().height / 2) * lrScale); + moveX = (bg->getContentSize().width) * lrScale; + break; + case PositionEnum::BottomRight: // bottom right + bg->setPosition({ winSize.width + ((bg->getContentSize().width / 2) * lrScale), (bg->getContentSize().height / 2) * lrScale}); + moveX = -((bg->getContentSize().width) * lrScale); + break; + } + std::string titleStr = data.title; + CCPoint titlePos; + switch (data.type) { + case EventType::smallChest: + titlePos = CCPoint(-65, 26); + Build::createSpriteName("chest_01_02_001.png").scale(0.45f).pos(45, -3).parent(node); + break; + case EventType::largeChest: + titlePos = CCPoint(-54, 26); + Build::createSpriteName("chest_02_02_001.png").scale(0.4f).pos(45, -3).parent(node); + break; + case EventType::List: + titlePos = CCPoint(-27, 27); + Build::createSpriteName("GJ_listAddBtn_001.png").pos(139, -8).parent(node); + break; + case EventType::Announcement: + titleStr = "Announcement"; + titlePos = CCPoint(-65, 26); + Build::create(data.title.c_str(), "bigFont.fnt").limitLabelWidth(200, 0.46f, 0.1f).anchorPoint(0, 0.5).pos(-65, 10).parent(node); + break; + default: + titlePos = CCPoint(-27, 23); + break; + } + if (data.type != EventType::smallChest && data.type != EventType::largeChest && data.type != EventType::Announcement) { + if (data.platformer) { + Build::createSpriteName("moon_small01_001.png").scale(.775f).pos(31, 18).parent(bg); + } else { + Build::createSpriteName((data.type == EventType::List) ? "diamond_small01_001.png" : "star_small01_001.png").scale(.775f).pos(31, 18).parent(bg); + } + if (data.type != EventType::List) { + Build::createSpriteName((!data.demon) ? getDifficultyIcon(starsum) : getDemonDifficultyIcon(starsum)) + .pos(26, 43) + .scale(.8f) + .parent(bg); + GJDifficultySprite* mythic = GJDifficultySprite::create( + static_cast((!data.demon) ? getDifficulty(starsum) : getDemonDifficulty(starsum)), + static_cast(0) + ); + mythic->updateFeatureState(GJFeatureState::Mythic); + switch (data.rate) { + default: + break; + } + + CCSprite* featured = cocos2d::CCSprite::createWithSpriteFrameName("GJ_featuredCoin_001.png"); + CCSprite* epic = cocos2d::CCSprite::createWithSpriteFrameName("GJ_epicCoin_001.png"); + CCSprite* legendary = cocos2d::CCSprite::createWithSpriteFrameName("GJ_epicCoin2_001.png"); + mythic->updateFeatureState(static_cast(4)); + + Build::create(std::to_string(stars).c_str(), "bigFont.fnt").pos(25, 18).anchorPoint(1, 0.5).scale(0.35f).parent(bg); + + featured->setPosition({26.f, 43.f}); + epic->setPosition({26.f, 43.f}); + legendary->setPosition({26.f, 43.f}); + mythic->setPosition({26.f, 43.f}); + featured->setScale(.8F); + epic->setScale(.8F); + legendary->setScale(.8F); + mythic->setScale(.8F); + + switch (data.type) { + default: + break; + case 1: // Featured + bg->addChild(featured); + break; + case 2: // Epic + bg->addChild(epic); + break; + case 3: // Legendary + bg->addChild(legendary); + + if (Mod::get()->template getSettingValue("customDifficultyFaces")) { + CCSprite* legendaryFace = nullptr; + if (!data.demon) { + if (starsum >= 10) { + std::string diffStr = std::to_string(static_cast(getDifficulty(starsum))); + auto name = "difficulty_0" + diffStr + "_legendaryIcon.png"; + legendaryFace = CCSprite::create((""_spr+name).c_str()); + } + } else { + if (starsum < 6) { + std::string diffStr = std::to_string(static_cast(getDemonDifficulty(starsum))); + auto name = "difficulty_0" + diffStr + "_legendaryIcon.png"; + legendaryFace = CCSprite::create((""_spr+name).c_str()); + } else { + std::string diffStr = std::to_string(static_cast(getDemonDifficulty(starsum))); + auto name = "difficulty_" + diffStr + "_legendaryIcon.png"; + legendaryFace = CCSprite::create((""_spr+name).c_str()); + } + } + + if (legendaryFace != nullptr) { + legendaryFace->setPosition({26.f, 48.f}); + legendaryFace->setScale(.8F); + bg->addChild(legendaryFace); + } + } + + break; + case 4: // Mythic + bg->addChild(mythic); + + if (Mod::get()->template getSettingValue("customDifficultyFaces")) { + CCSprite* mythicFace = nullptr; + if (!data.demon) { + if (starsum >= 10) { + std::string diffStr = std::to_string(static_cast(getDifficulty(starsum))); + auto name = "difficulty_0" + diffStr + "_mythicIcon.png"; + mythicFace = CCSprite::create((""_spr+name).c_str()); + } + } else { + if (starsum < 6) { + std::string diffStr = std::to_string(static_cast(getDemonDifficulty(starsum))); + auto name = "difficulty_0" + diffStr + "_mythicIcon.png"; + mythicFace = CCSprite::create((""_spr+name).c_str()); + } else { + std::string diffStr = std::to_string(static_cast(getDemonDifficulty(starsum))); + auto name = "difficulty_" + diffStr + "_mythicIcon.png"; + mythicFace = CCSprite::create((""_spr+name).c_str()); + } + } + + if (mythicFace != nullptr) { + mythicFace->setPosition({26.f, 48.f}); + mythicFace->setScale(.8F); + bg->addChild(mythicFace); + } + } + + break; + } + + auto verifiedCoinSpr1 = cocos2d::CCSprite::createWithSpriteFrameName("GJ_coinsIcon2_001.png"); + verifiedCoinSpr1->setScale(0.35f); + auto verifiedCoinSpr2 = cocos2d::CCSprite::createWithSpriteFrameName("GJ_coinsIcon2_001.png"); + verifiedCoinSpr2->setScale(0.35f); + auto verifiedCoinSpr3 = cocos2d::CCSprite::createWithSpriteFrameName("GJ_coinsIcon2_001.png"); + verifiedCoinSpr3->setScale(0.35f); + + if (areCoinsVerified == 0) { + verifiedCoinSpr1->setColor({255, 175, 75}); + verifiedCoinSpr2->setColor({255, 175, 75}); + verifiedCoinSpr3->setColor({255, 175, 75}); + } + + if (coins == 1 || coins == 3) { + verifiedCoinSpr2->setPosition({ -52, -22 }); + node->addChild(verifiedCoinSpr2); + + if (coins == 3) { + verifiedCoinSpr1->setPosition({ -60, -22 }); + verifiedCoinSpr3->setPosition({ -44, -22 }); + + node->addChild(verifiedCoinSpr1); + node->addChild(verifiedCoinSpr3); + } + } else if (coins == 2) { + verifiedCoinSpr1->setPosition({ -55, -22 }); + verifiedCoinSpr2->setPosition({ -48, -22 }); + + node->addChild(verifiedCoinSpr1); + node->addChild(verifiedCoinSpr2); + } + } else { + + } + } else if (data.type == EventType::List) { + } + Build::create(titleStr.c_str(), "goldFont.fnt").scale(0.575F).pos(titlePos).anchorPoint(0, 0.5).parent(node); + + if (Mod::get()->getSettingValue("sfx")) { + float volume = (Mod::get()->getSettingValue("sfx-link")) ? GameManager::get()->m_sfxVolume : Mod::get()->getSettingValue("sfx-volume"); + if (data.type != EventType::smallChest && data.type != EventType::largeChest) { + FMODAudioEngine::get()->playEffect("crystal01.ogg", 1, 1, volume); + } else { + FMODAudioEngine::get()->playEffect("reward01.ogg", 1, 1, volume); + } + } + + bg->runAction(CCSequence::create( + CCEaseOut::create(CCMoveBy::create(0.5f, { moveX, 0.0f }), 0.6f), + CCDelayTime::create(Mod::get()->getSettingValue("time")), + CCEaseIn::create(CCMoveBy::create(0.5f, { -moveX, 0.0f }), 0.6f), + CCDelayTime::create(0.5F), + CCCallFunc::create(this, callfunc_selector(EventsPush::destroySelf)), + nullptr + )); + + this->setZOrder(scene->getHighestChildZ() + 10); + this->setID("EventsPush"_spr); + return true; +} + +void EventsPush::processNextEvent(CCScene *self) { + if (!g_eventQueue.empty()) { + auto data = std::move(g_eventQueue.front()); + g_eventQueue.pop(); + g_processingEvents = true; + auto layer = EventsPush::create(data); + GDUtils::Events::OnRate(data.type).send(data); + if (layer && self) { + self->addChild(layer); + } + } +} + +void EventsPush::pushRateLevel(CCScene *self, EventData data) { + log::debug( + "EventData [\ndemon = {};\nstarsum = {};\nstars = {};\nrate = {};\ntype = {};\ntitle = {};\nsprite = {};\nlevel_name = {};\nlevel_creator = {};\ncoins = {};\nverified_coins = {};\nplatformer = {};\nlevel_id = {};\nlevels_list = {};\nmaxToCompleteList = {};\n]", + data.demon, + data.starsum, + data.stars, + data.rate, + GDUtils::Events::RateEvent::typeToInt(data.type), + data.title, + data.sprite, + data.level_name, + data.level_creator, + data.coins, + data.verified_coins, + data.platformer, + data.level_id, + data.levels_list, + data.maxToCompleteList + ); + g_eventQueue.push(data); + // Start processing events if no events are being processed currently. + if (!g_processingEvents) { + processNextEvent(self); + } +} +/* +payload = { + "type": 7, + "title": "test" +} +*/ + +void EventsPush::callCancel() { + if (g_finishTx) { + (void)g_finishTx->trySend(std::move(true)); + } +} +void EventsPush::eventCompletedCallback(CCScene *self) { + g_processingEvents = false; + processNextEvent(self); +} + +std::string getNodeName(cocos2d::CCObject* node) { +#ifdef GEODE_IS_WINDOWS + return typeid(*node).name() + 6; +#else + { + std::string ret; + + int status = 0; + auto demangle = abi::__cxa_demangle(typeid(*node).name(), 0, 0, &status); + if (status == 0) { + ret = demangle; + } + free(demangle); + + return ret; + } +#endif +} + +static std::string s_currentLayer; + +void EventsPush::setTx(arc::mpsc::Sender tx) { + g_finishTx = std::move(tx); +} + +bool EventsPush::canSendEvent(CCScene* scene) { + auto layer = scene->getChildren()->objectAtIndex(0); + std::string layerName = getNodeName(layer); + if (layerName == "cocos2d::CCLayerColor") return false; + if (layerName == "LoadingLayer") return false; + bool everywhereElse = Mod::get()->getSettingValue("everywhereElse"); + bool inLevels = Mod::get()->getSettingValue("inLevels"); + bool inEditor = Mod::get()->getSettingValue("inEditor"); + bool inPlatformers = Mod::get()->getSettingValue("inPlatformers"); + bool pushEvent = true; + if (layerName == "PlayLayer") { + if (!inLevels) pushEvent = false; + } + if (layerName == "LevelEditorLayer" && !inEditor) { + pushEvent = false; + } + if ((layerName != "LevelEditorLayer" && layerName != "PlayLayer") && !everywhereElse) { + pushEvent = false; + } + return pushEvent; +} +$execute { + GDUtils::Events::RateEvent().listen([](EventData const& e) { + auto json = matjson::Serialize::toJson(e); + std::string toStr = json.dump(0); + AMQT::sendToTx(std::move(toStr)); + return ListenerResult::Stop; + }).leak(); + // ".leak() is for global listeners pretty much" +} diff --git a/src/modules/events/EventsPush.hpp b/src/modules/events/EventsPush.hpp new file mode 100644 index 0000000..612c585 --- /dev/null +++ b/src/modules/events/EventsPush.hpp @@ -0,0 +1,58 @@ +#pragma once +#include +#include +#include "../../include/RateEvent.hpp" + +class EventsPush : public CCLayer { + protected: + bool m_alreadyDead = false; + GJGameLevel* level = GJGameLevel::create(); + EventType eventType = EventType::NA; + virtual bool init(EventData data); + void destroySelf() { + m_alreadyDead = true; + auto scene = CCDirector::get()->getRunningScene(); + if (scene->getChildByID("EventsPush"_spr) != nullptr) { + scene->removeChildByID("EventsPush"_spr); + } + eventCompletedCallback(scene); + } + public: + void onExit() override { + CCLayer::onExit(); + if (!m_alreadyDead) { + callCancel(); + } + } + void onClickBtn(CCObject*); + static void pushRateLevel(CCScene* self, std::string data) { + auto jsonStr = matjson::parse(data); + if (jsonStr.isErr()) { + log::error("Couldn't parse JSON string: {}", jsonStr.unwrapErr()); + } + auto eventData = matjson::Serialize::fromJson(jsonStr.unwrapOrDefault()); + if (eventData.isErr()) { + log::error("Couldn't parse JSON to EventData: {}", eventData.unwrapErr()); + } + pushRateLevel( + self, + eventData.unwrapOrDefault() + ); + } + static void pushRateLevel(CCScene* self, EventData data); + static void processNextEvent(CCScene* self); + static void eventCompletedCallback(CCScene* self); + static void callCancel(); + static std::vector getWords(std::string s, std::string delim); + static bool canSendEvent(CCScene* self); + static EventsPush* create(EventData data) { + auto ret = new EventsPush(); + if (ret && ret->init(data)) { + ret->autorelease(); + return ret; + } + CC_SAFE_DELETE(ret); + return nullptr; + } + static void setTx(arc::mpsc::Sender tx); +}; diff --git a/src/modules/secrets/BadApple.cpp b/src/modules/secrets/BadApple.cpp new file mode 100644 index 0000000..7def2c9 --- /dev/null +++ b/src/modules/secrets/BadApple.cpp @@ -0,0 +1,38 @@ +// SPOILERSSS +#include "BadApple.hpp" +#ifdef GEODE_IS_WINDOWS +#include "VideoPlayer.hpp" +#else // mac +#include +#include +#endif +BadApple* BadApple::create() { + auto pRet = new BadApple(); + if (pRet && pRet->BadApple::init()) { + pRet->autorelease(); + return pRet; + } + CC_SAFE_DELETE(pRet); + return nullptr; +}; + +bool BadApple::init() { + //#ifndef GEODE_IS_MACOS + #ifdef GEODE_IS_WINDOWS + videoplayer::VideoPlayer* player = videoplayer::VideoPlayer::create(Mod::get()->getSaveDir() / "bad_apple.mpg", true); + CCDirector* director = CCDirector::sharedDirector(); + CCSize size = director->getWinSize(); + player->setPosition(size.width / 2, size.height / 2); + player->setContentSize({ size.width - 40, size.height - 40 }); + player->setZOrder(-1); + this->addChild(player); + #endif + setKeypadEnabled(true); + + return true; +} + +void BadApple::keyBackClicked() { + setKeypadEnabled(false); + CCDirector::sharedDirector()->popSceneWithTransition(0.5f, PopTransition::kPopTransitionFade); +} diff --git a/src/Secret/BadApple.h b/src/modules/secrets/BadApple.hpp similarity index 65% rename from src/Secret/BadApple.h rename to src/modules/secrets/BadApple.hpp index 0451d05..8d0a46d 100644 --- a/src/Secret/BadApple.h +++ b/src/modules/secrets/BadApple.hpp @@ -1,9 +1,8 @@ // SPOILERSSS #pragma once -#include "../includes.h" -#include "VideoPlayer.hpp" +#include -class BadApple : public CCLayer { +class BadApple : public cocos2d::CCLayer { protected: virtual bool init(); virtual void keyBackClicked(); diff --git a/src/Secret/COTE.cpp b/src/modules/secrets/COTE.cpp similarity index 79% rename from src/Secret/COTE.cpp rename to src/modules/secrets/COTE.cpp index b7678c2..9a6e4a6 100644 --- a/src/Secret/COTE.cpp +++ b/src/modules/secrets/COTE.cpp @@ -1,7 +1,7 @@ // SPOILERSSS -#include "COTE.h" +#include "COTE.hpp" #ifdef GEODE_IS_WINDOWS -#include "../Utils/DownloadManager.h" +#include "VideoPlayer.hpp" #else // mac #include #include @@ -21,7 +21,7 @@ bool COTE::init() { auto menu = CCMenu::create(); //#ifndef GEODE_IS_MACOS #ifdef GEODE_IS_WINDOWS - videoplayer::VideoPlayer* player = videoplayer::VideoPlayer::create("./gdutils/cote.mpg", true); + videoplayer::VideoPlayer* player = videoplayer::VideoPlayer::create(Mod::get()->getSaveDir() / "cote.mpg", true); CCDirector* director = CCDirector::sharedDirector(); CCSize size = director->getWinSize(); player->setPosition(size.width / 2, size.height / 2); @@ -30,17 +30,12 @@ bool COTE::init() { this->addChild(player); #endif setKeypadEnabled(true); - return true; } void COTE::keyBackClicked() { setKeypadEnabled(false); - - #ifdef GEODE_IS_UNKNOWN - GameSoundManager::sharedManager()->playBackgroundMusic("./Resources/secretLoop02.mp3", true, false); - #endif CCDirector::sharedDirector()->popSceneWithTransition(0.5f, PopTransition::kPopTransitionFade); } -// cote time \ No newline at end of file +// cote time diff --git a/src/Secret/COTE.h b/src/modules/secrets/COTE.hpp similarity index 64% rename from src/Secret/COTE.h rename to src/modules/secrets/COTE.hpp index 7f3bfdd..75f4e39 100644 --- a/src/Secret/COTE.h +++ b/src/modules/secrets/COTE.hpp @@ -1,13 +1,12 @@ // SPOILERSSS #pragma once -#include "../includes.h" -#include "VideoPlayer.hpp" +#include -class COTE : public CCLayer { +class COTE : public cocos2d::CCLayer { protected: virtual bool init(); virtual void keyBackClicked(); public: static COTE* create(); -}; \ No newline at end of file +}; diff --git a/src/Secret/VideoPlayer.cpp b/src/modules/secrets/VideoPlayer.cpp similarity index 100% rename from src/Secret/VideoPlayer.cpp rename to src/modules/secrets/VideoPlayer.cpp diff --git a/src/Secret/VideoPlayer.hpp b/src/modules/secrets/VideoPlayer.hpp similarity index 100% rename from src/Secret/VideoPlayer.hpp rename to src/modules/secrets/VideoPlayer.hpp diff --git a/src/modules/settings/Credits.cpp b/src/modules/settings/Credits.cpp new file mode 100644 index 0000000..d05fadd --- /dev/null +++ b/src/modules/settings/Credits.cpp @@ -0,0 +1,27 @@ +#include "Credits.hpp" +#include +#include + +SettingNodeV3* CreditsValue::createNode(float width) { + return CreditsNode::create(static_pointer_cast(shared_from_this()), width); +} + +bool CreditsNode::init(std::shared_ptr setting, float width) { + if (!SettingNodeV3::init(setting, width)) + return false; + this->setContentSize({ width, 40.f }); + getNameLabel()->setVisible(false); + getButtonMenu()->setContentWidth(width); + Build::create("Credits", "bigFont.fnt", "GJ_button_01.png").scale(0.6f).intoMenuItem([]() { + CreditsMenu::create()->show(); + }).parentAtPos(getButtonMenu(), Anchor::Center, {-55, 0}); + Build::create("Beta Testers", "bigFont.fnt", "GJ_button_01.png").scale(0.6f).intoMenuItem([]() { + CreditsBetaMenu::create()->show(); + }).parentAtPos(getButtonMenu(), Anchor::Center, {85, 0}); + getButtonMenu()->updateLayout(); + return true; +} + +$execute { + (void)Mod::get()->registerCustomSettingType("credit-buttons", &CreditsValue::parse); +} diff --git a/src/modules/settings/Credits.hpp b/src/modules/settings/Credits.hpp new file mode 100644 index 0000000..b8744a9 --- /dev/null +++ b/src/modules/settings/Credits.hpp @@ -0,0 +1,49 @@ +#pragma once +#include + +class CreditsValue : public SettingV3 { +public: + static Result> parse(std::string const& key, std::string const& modID, matjson::Value const& json) { + auto res = std::make_shared(); + auto root = checkJson(json, "SettingCreditsValue"); + res->init(key, modID, root); + res->parseNameAndDescription(root); + res->parseEnableIf(root); + root.checkUnknownKeys(); + return root.ok(std::static_pointer_cast(res)); + } + + bool load(matjson::Value const& json) override { + return true; + } + bool save(matjson::Value& json) const override { + return true; + } + bool isDefaultValue() const override { + return true; + } + void reset() override {} + SettingNodeV3* createNode(float width) override; +}; +class CreditsNode : public SettingNodeV3 { +protected: + bool init(std::shared_ptr setting, float width); + void onCommit() override {} + void onResetToDefault() override {} +public: + bool hasUncommittedChanges() const override { + return false; + } + bool hasNonDefaultValue() const override { + return false; + } + static CreditsNode* create(std::shared_ptr setting, float width) { + auto ret = new CreditsNode(); + if (ret && ret->init(setting, width)) { + ret->autorelease(); + return ret; + } + CC_SAFE_DELETE(ret); + return nullptr; + } +}; diff --git a/src/modules/settings/Position.cpp b/src/modules/settings/Position.cpp new file mode 100644 index 0000000..814f3b5 --- /dev/null +++ b/src/modules/settings/Position.cpp @@ -0,0 +1,40 @@ +#include "Position.hpp" + +SettingNodeV3* PositionValue::createNode(float width) { + return PositionNode::create(static_pointer_cast(shared_from_this()), width); +} + +bool PositionNode::init(std::shared_ptr setting, float width) { + if (!SettingValueNodeV3::init(setting, width)) + return false; + this->setContentSize({ width, 70.f }); + getButtonMenu()->setContentSize({width / 2, 45.F}); + CCSprite* toggleOn = Build::createSpriteName("GJ_checkOn_001.png").scale(0.7f).collect(); + CCSprite* toggleOff = Build::createSpriteName("GJ_checkOff_001.png").scale(0.7f).collect(); + auto currentCorner = static_cast(this->getValue()); + Build::createToggle(toggleOn, toggleOff, [this](auto) { + onCornerClick(PositionEnum::TopLeft); + }).toggle(currentCorner != PositionEnum::TopLeft).parentAtPos(getButtonMenu(), Anchor::TopLeft).store(m_tlBtn); + Build::createToggle(toggleOn, toggleOff, [this](auto) { + onCornerClick(PositionEnum::TopRight); + }).toggle(currentCorner != PositionEnum::TopRight).parentAtPos(getButtonMenu(), Anchor::TopRight).store(m_trBtn); + Build::createToggle(toggleOn, toggleOff, [this](auto) { + onCornerClick(PositionEnum::BottomLeft); + }).toggle(currentCorner != PositionEnum::BottomLeft).parentAtPos(getButtonMenu(), Anchor::BottomLeft).store(m_blBtn); + Build::createToggle(toggleOn, toggleOff, [this](auto) { + onCornerClick(PositionEnum::BottomRight); + }).toggle(currentCorner != PositionEnum::BottomRight).parentAtPos(getButtonMenu(), Anchor::BottomRight).store(m_brBtn); + getButtonMenu()->updateLayout(); + getButtonMenu()->setAnchorPoint({0.5, 0.5}); + getButtonMenu()->updateAnchoredPosition(Anchor::Center, {0, 0}); + this->getNameLabel()->setVisible(false); + this->updateState(nullptr); + return true; +} + +$execute { + auto ret = Mod::get()->registerCustomSettingType("notification-placement", &PositionValue::parse); + if (!ret) { + log::error("Unable to register setting type: {}", ret.unwrapErr()); + } +} diff --git a/src/modules/settings/Position.hpp b/src/modules/settings/Position.hpp new file mode 100644 index 0000000..e2a7f91 --- /dev/null +++ b/src/modules/settings/Position.hpp @@ -0,0 +1,90 @@ +#pragma once +#include +enum class PositionEnum : int { + TopLeft, + TopRight, + BottomLeft, + BottomRight +}; +template <> +struct matjson::Serialize { + static matjson::Value toJson(PositionEnum const& value) { + switch (value) { + case PositionEnum::TopLeft: + return 1; + case PositionEnum::TopRight: + return 2; + case PositionEnum::BottomLeft: + return 3; + default: + case PositionEnum::BottomRight: + return 4; + } + } + static Result fromJson(matjson::Value const& value) { + switch (value.asInt().unwrapOrDefault()) { + case 1: return Ok(PositionEnum::TopLeft); + case 2: return Ok(PositionEnum::TopRight); + case 3: return Ok(PositionEnum::BottomLeft); + case 4: return Ok(PositionEnum::BottomRight); + default: return Err("Invalid PositionEnum value '{}'", value.asInt().unwrapOrDefault()); + } + } + static bool is_json(matjson::Value const& json) { + return json.isNumber() || json.isString(); + } +}; + +class PositionValue : public SettingBaseValueV3 { +public: + static Result> parse(std::string const& key, std::string const& modID, matjson::Value const& json) { + auto res = std::make_shared(); + auto root = checkJson(json, "PositionValue"); + res->parseBaseProperties(key, modID, root); + root.checkUnknownKeys(); + return root.ok(std::static_pointer_cast(res)); + } + SettingNodeV3* createNode(float width) override; +}; + + +template <> +struct geode::SettingTypeForValueType { + using SettingType = PositionValue; +}; + +class PositionNode : public SettingValueNodeV3 { +protected: + CCMenuItemToggler* m_tlBtn; + CCMenuItemToggler* m_trBtn; + CCMenuItemToggler* m_blBtn; + CCMenuItemToggler* m_brBtn; + bool init(std::shared_ptr setting, float width); + void updateState(CCNode* invoker) override { + SettingValueNodeV3::updateState(invoker); + } + void onCornerClick(PositionEnum value) { + m_tlBtn->toggle(true); + m_trBtn->toggle(true); + m_blBtn->toggle(true); + m_brBtn->toggle(true); + this->setValue(value, nullptr); + }; + void onResetToDefault() override { + m_tlBtn->toggle(true); + m_trBtn->toggle(true); + m_blBtn->toggle(true); + m_brBtn->toggle(false); + this->setValue(PositionEnum::BottomRight, nullptr); + } +public: + static PositionNode* create(std::shared_ptr setting, float width) { + auto ret = new PositionNode(); + if (ret && ret->init(setting, width)) { + ret->autorelease(); + return ret; + } + CC_SAFE_DELETE(ret); + return nullptr; + } +}; diff --git a/src/modules/settings/Test.cpp b/src/modules/settings/Test.cpp new file mode 100644 index 0000000..7901827 --- /dev/null +++ b/src/modules/settings/Test.cpp @@ -0,0 +1,157 @@ +#include +#include "../../include/RateEvent.hpp" + +using namespace geode::prelude; + +/* + --- Test --- +*/ + +int cycleTypes = -1; + +class TestValue : public SettingV3 { +public: + static Result> parse(std::string const& key, std::string const& modID, matjson::Value const& json) { + auto res = std::make_shared(); + auto root = checkJson(json, "TestValue"); + res->init(key, modID, root); + res->parseNameAndDescription(root); + res->parseEnableIf(root); + root.checkUnknownKeys(); + return root.ok(std::static_pointer_cast(res)); + } + + bool load(matjson::Value const& json) override { + return true; + } + bool save(matjson::Value& json) const override { + return true; + } + bool isDefaultValue() const override { + return true; + } + void reset() override {} + SettingNodeV3* createNode(float width) override; +}; + +class TestNode : public SettingNodeV3 { +protected: + bool init(std::shared_ptr setting, float width) { + if (!SettingNodeV3::init(setting, width)) + return false; + this->setContentSize({ width, 35.f }); + getButtonMenu()->setContentWidth(width); + Build::create("Test", "bigFont.fnt").scale(0.6f).parentAtPos(getButtonMenu(), Anchor::Left, {42, 0}); + Build::createSpriteName("GJ_playBtn2_001.png").scale(0.4f).intoMenuItem([]() { + auto scene = CCDirector::sharedDirector()->getRunningScene(); + cycleTypes = cycleTypes + 1; + if (cycleTypes > 4) cycleTypes = 0; + bool newRate = Mod::get()->getSettingValue("newRate"); + bool daily = Mod::get()->getSettingValue("daily"); + bool weekly = Mod::get()->getSettingValue("weekly"); + if (!newRate && cycleTypes == 0) cycleTypes = 1; + if (!daily && cycleTypes == 1) cycleTypes = 2; + if (!weekly && cycleTypes == 2) cycleTypes = 3; + if (!newRate && !daily && !weekly) { + cycleTypes = 5; + } + EventData data; + data.type = EventType::Rate; + data.rate = 1; + data.coins = 3; + data.verified_coins = true; + data.level_creator = "by RobTop"; + //{'demon': 1, 'type': 1, 'starsum': 6, 'stars': 10, 'rate': 1, 'title': 'BRAINPOWER Let the base kick!', 'level_name': 'O-oooooooooo AAAAE-A-A-I-A-U-', 'level_creator': ' JO-oooooooooooo AAE-O-A-A-U-U-A- E-eee-ee-eee AAAAE-A-E-I-E-A- JO-ooo-oo-oo-oo EEEEO-A-AAA-AAAA', 'sprite': 'GJ_square01.png'} + switch (cycleTypes) { + case 0: // new rate + data.title = "New Rated Level !"; + data.stars = 1; + data.starsum = 10; + data.level_name = "Stereo Madness"; + break; + case 1: // daily + data.title = "New Daily Level !"; + data.type = EventType::Daily; + data.stars = 9; + data.starsum = 40; + data.level_name = "Cycles"; + break; + case 2: // weekly + data.title = "New Weekly Level !"; + data.demon = true; + data.type = EventType::Weekly; + data.stars = 14; + data.starsum = 7; + data.level_name = "Theory of Everything 2"; + data.sprite = "GJ_square05.png"; + break; + case 3: // Legendary rating test + data.title = "New Rated Level !"; + data.stars = 3; + data.starsum = 20; + data.level_name = "Oh no it's happening"; + data.level_creator = "by ..."; + data.rate = 3; + break; + case 4: // Mythic rating test + data.title = "New Rated Level !"; + data.stars = 100; + data.starsum = 20; + data.type = EventType::Event; + data.level_name = "FIRE IN THE HOLE!"; + data.level_creator = "by GD Community"; + data.rate = 4; + break; + case 5: // in case the user disables everything + data.title = "New Rated Level !"; + data.demon = true; + data.stars = 2; + data.starsum = 6; + data.level_name = "Back On Track"; + data.rate = 1; + break; + default: // not possible + data.title = "hi there hxd user, ghidra user or ida user. (or hi RobTop) Here's a little easter egg for you :)"; + data.demon = true; + data.stars = 14; + data.starsum = 7; + data.sprite = "This will crash! :D"; + break; + } + GDUtils::Events::RateEvent::emit(data); + }).parentAtPos(getButtonMenu(), Anchor::Right, {-15, 0}); + getButtonMenu()->updateLayout(); + getNameLabel()->setVisible(false); + return true; + } + void onCommit() override {} + void onResetToDefault() override {} + +public: + bool hasUncommittedChanges() const override { + return false; + } + bool hasNonDefaultValue() const override { + return false; + } + static TestNode* create(std::shared_ptr setting, float width) { + auto ret = new TestNode(); + if (ret && ret->init(setting, width)) { + ret->autorelease(); + return ret; + } + CC_SAFE_DELETE(ret); + return nullptr; + } + std::shared_ptr getSetting() const { + return std::static_pointer_cast(SettingNodeV3::getSetting()); + } +}; + +SettingNodeV3* TestValue::createNode(float width) { + return TestNode::create(static_pointer_cast(shared_from_this()), width); +} + +$execute { + (void)Mod::get()->registerCustomSettingType("test-notification", &TestValue::parse); +} diff --git a/src/network/AMQClient.cpp b/src/network/AMQClient.cpp new file mode 100644 index 0000000..c555136 --- /dev/null +++ b/src/network/AMQClient.cpp @@ -0,0 +1,290 @@ +#include "AMQClient.hpp" + +bool show_connected = false; +std::queue dataQueue; +static std::optional> s_tx; + +void AMQT::setTx(arc::mpsc::Sender tx) { + s_tx = std::move(tx); +} + +bool AMQT::sendHeartbeat() { + auto now = std::chrono::steady_clock::now(); + auto lastDuration = std::chrono::duration_cast(now - m_lastHeartbeatTime).count(); + // every 10 seconds + if (lastDuration >= 10) { + std::lock_guard lock(m_mutex); + if (isConnected()) { + // because WHY CANT I USE amqp_heartbeat_send!?!? we will decide to manually construct it... + amqp_frame_t frame; + frame.channel = 0; // or 1? idk... amqp_socket.c says 0 + frame.frame_type = AMQP_FRAME_HEARTBEAT; + auto res = amqp_send_frame(m_connection, &frame); + if (AMQP_STATUS_OK != res) { + log::warn("Failed to send heartbeat: {}", amqp_error_string2(res)); + return false; + } else { + //log::debug("Sent heartbeat!"); + m_lastHeartbeatTime = now; + return true; + } + } + } + return true; +} + +std::string AMQReplyToString(amqp_response_type_enum val) { + switch (val) { + case AMQP_RESPONSE_NONE: + default: + return "AMQP_RESPONSE_NONE"; + case AMQP_RESPONSE_NORMAL: + return "AMQP_RESPONSE_NORMAL"; + case AMQP_RESPONSE_LIBRARY_EXCEPTION: + return "AMQP_RESPONSE_LIBRARY_EXCEPTION"; + case AMQP_RESPONSE_SERVER_EXCEPTION: + return "AMQP_RESPONSE_SERVER_EXCEPTION"; + } +} +std::string AMQErrorToString(amqp_rpc_reply_t reply) { + std::string errorStr; + switch (reply.reply_type) { + case AMQP_RESPONSE_NONE: errorStr = "No response"; break; + case AMQP_RESPONSE_LIBRARY_EXCEPTION: errorStr = amqp_error_string2(reply.library_error); break; + case AMQP_RESPONSE_SERVER_EXCEPTION: + if (reply.reply.id == AMQP_CONNECTION_CLOSE_METHOD) { + amqp_connection_close_t* m = (amqp_connection_close_t*) reply.reply.decoded; + errorStr = (char*)m->reply_text.bytes; + } else { + errorStr = "Unknown Error"; + } + break; + } + return fmt::format("{} ({})", errorStr, AMQReplyToString(reply.reply_type)); +} +void AMQT::setupChannel() { + bool showPastNotifs = false; + std::string queueName = fmt::format("amqp-queue-{}", client_id); + { + amqp_channel_open(m_connection, 1); + auto reply = amqp_get_rpc_reply(m_connection); + if (reply.reply_type != AMQP_RESPONSE_NORMAL) { + log::error("Error opening channel: {}", AMQErrorToString(reply)); + return; + } + } + // https://stackoverflow.com/questions/72237234/declare-a-queue-with-x-max-length-programmatically-using-rabbitmq-c + amqp_queue_declare_ok_t *r; + if (showPastNotifs) { + // this is absolutely cursed + /*amqp_table_entry_t q_arg_n_entries[2]; + q_arg_n_entries[0] = (amqp_table_entry_t) { + .key = amqp_cstring_bytes("x-queue-type"), + .value = {.kind = AMQP_FIELD_KIND_UTF8, .value = {.bytes = amqp_cstring_bytes("classic")}} + }; + + //86400000 + // 2 days + q_arg_n_entries[1] = (amqp_table_entry_t) { + .key = amqp_cstring_bytes("x-expires"), + .value = {.kind = AMQP_FIELD_KIND_I32, .value = {.i32 = 172800000}} + }; + amqp_table_t q_arg_table = {.num_entries = 2, .entries = q_arg_n_entries};*/ + + amqp_table_entry_t q_arg_n_entries[1]; + // 1 day + q_arg_n_entries[0] = (amqp_table_entry_t) { + .key = amqp_cstring_bytes("x-expires"), + .value = {.kind = AMQP_FIELD_KIND_I32, .value = {.i32 = 86400000}} + }; + amqp_table_t q_arg_table = {.num_entries = 1, .entries = q_arg_n_entries}; + r = amqp_queue_declare( + m_connection, 1, + amqp_cstring_bytes(queueName.c_str()), + 0, 1, 0, 0, + q_arg_table + ); + } else { + amqp_queue_delete(m_connection, 1, amqp_cstring_bytes(queueName.c_str()), 0, 0); + amqp_get_rpc_reply(m_connection); + r = amqp_queue_declare( + m_connection, 1, + amqp_cstring_bytes(queueName.c_str()), + 0, 0, 0, 1, + amqp_empty_table + ); + } + { + auto reply = amqp_get_rpc_reply(m_connection); + if (reply.reply_type != AMQP_RESPONSE_NORMAL) { + log::error("Error declaring queue: {}", AMQErrorToString(reply)); + return; + } + } + amqp_bytes_t queueBytes = amqp_bytes_malloc_dup(r->queue); + if (queueBytes.bytes == NULL) { + log::error("Couldn't copy queue name (Out of memory?)"); + return; + } + { + amqp_queue_bind(m_connection, 1, + queueBytes, amqp_cstring_bytes("amq.topic"), + amqp_cstring_bytes("rate.#"), amqp_empty_table + ); + auto reply = amqp_get_rpc_reply(m_connection); + if (reply.reply_type != AMQP_RESPONSE_NORMAL) { + log::error("Error binding queue: {}", AMQErrorToString(reply)); + return; + } + } + { + auto res = amqp_basic_consume(m_connection, 1, queueBytes, amqp_empty_bytes, 0, 1, 0, amqp_empty_table); + auto reply = amqp_get_rpc_reply(m_connection); + + if (reply.reply_type != AMQP_RESPONSE_NORMAL) { + log::error("Error consuming in queue: {}", AMQErrorToString(reply)); + return; + } + if (res) { + std::string data((char*)res->consumer_tag.bytes, res->consumer_tag.len); + show_connected = true; + log::info("Subscribed to Rate Notifications! (Consumer Tag: {})", data); + } + } + + bool forceReconnection = false; + auto lastReconnectTime = std::chrono::steady_clock::now(); + + struct timeval timeout = {10, 0}; + + while (isConnected()) { + // this is just an extra so we're sure that we're always connected + if (!sendHeartbeat()) { + log::error("Couldn't send heartbeat, disconnecting..."); + break; + } + + if (forceReconnection) { + auto now = std::chrono::steady_clock::now(); + auto lastDuration = std::chrono::duration_cast(now - lastReconnectTime).count(); + if (lastDuration >= 600) { + log::info("Forcing a reconnection..."); + break; + } + } + + amqp_envelope_t envelope; + amqp_maybe_release_buffers(m_connection); + + auto ret = amqp_consume_message(m_connection, &envelope, &timeout, 0); + if (AMQP_RESPONSE_NORMAL != ret.reply_type) { + if (AMQP_RESPONSE_LIBRARY_EXCEPTION == ret.reply_type && AMQP_STATUS_HEARTBEAT_TIMEOUT == ret.library_error) { + log::error("Heartbeat failed, initiating a reconnection!"); + break; + } else if (AMQP_RESPONSE_LIBRARY_EXCEPTION == ret.reply_type && AMQP_STATUS_UNEXPECTED_STATE == ret.library_error) { + amqp_frame_t frame; + if (AMQP_STATUS_OK != amqp_simple_wait_frame(m_connection, &frame)) { + log::error("Failed to wait for frame after unexpected state: {}", AMQErrorToString(ret)); + break; + } + if (AMQP_FRAME_METHOD == frame.frame_type) { + if (frame.payload.method.id == AMQP_BASIC_ACK_METHOD) continue; + if (frame.payload.method.id == AMQP_BASIC_RETURN_METHOD) { + { + log::info("Got frame payload, re-reading message based on channel..."); + amqp_message_t message; + ret = amqp_read_message(m_connection, frame.channel, &message, 0); + if (AMQP_RESPONSE_NORMAL != ret.reply_type) { + log::error("Error reading returned message: {}", AMQErrorToString(ret)); + break; + } + std::string data((char*)message.body.bytes, message.body.len); + if (s_tx) { + (void)s_tx->trySend(std::move(data)); + } + amqp_destroy_message(&message); + } + continue; + } else { + log::error("(Frame ID: {}) Error consuming message: {}", frame.payload.method.id, AMQErrorToString(ret)); + break; + } + } else { + log::error("Response wasn't normal! {}", AMQErrorToString(ret)); + } + } else if (AMQP_RESPONSE_LIBRARY_EXCEPTION == ret.reply_type && AMQP_STATUS_TIMEOUT == ret.library_error) { + // honestly how has no one gotten this issue!? + continue; + } else { + log::error("Error consuming message: {}", AMQErrorToString(ret)); + break; + } + } else { + log::info("call rate event"); + std::string data((char*)envelope.message.body.bytes, envelope.message.body.len); + if (s_tx) { + (void)s_tx->trySend(std::move(data)); + } + amqp_destroy_envelope(&envelope); + } + } + m_connected = false; + m_cv.notify_all(); +} + +void AMQT::connect() { + m_remainingAttempts++; + while (m_remainingAttempts-- > 0) { + log::info("Starting AMQP..."); + if (m_remainingAttempts != MAX_RECONNECT_ATTEMPTS) { + int currentAttempt = MAX_RECONNECT_ATTEMPTS - m_remainingAttempts + 1; + int delayMs = 3000 * std::pow(2, currentAttempt - 1); + log::warn("Disconnected from server. Attempting to reconnect... ({} left, reconnecting in {} seconds)", m_remainingAttempts, (float)((delayMs / 1000.F))); + std::this_thread::sleep_for(std::chrono::milliseconds(delayMs)); + } + m_connection = amqp_new_connection(); + amqp_socket_t* socket = amqp_tcp_socket_new(m_connection); + if (!socket) { + log::error("Couldn't create TCP connection"); + continue; + } + int status; + status = amqp_socket_open(socket, AMQ_HOST, AMQ_PORT); + if (status != AMQP_STATUS_OK) { + log::error("Error opening socket! {}", amqp_error_string2(status)); + continue; + } + if (socket && m_connection) { + // heartbeat every 10 seconds! + amqp_rpc_reply_t reply = amqp_login(m_connection, "/", 0, 131072, 10, AMQP_SASL_METHOD_PLAIN, AMQ_USER, AMQ_PASS); + if (reply.reply_type != AMQP_RESPONSE_NORMAL) { + const char* errorStr = nullptr; + log::error("Couldn't login to AMQP server! {}", AMQErrorToString(reply)); + continue; + } + m_connected = true; + setupChannel(); + disconnect(); + } + } + log::error("Maximum reconnection attempts reached, cannot reconnect. Restart the game to reconnect to the MQTT server."); +} +bool AMQT::genConnected() { + return show_connected; +} +void AMQT::setGenConnected(bool value) { + show_connected = value; +} +std::queue AMQT::getQueue() { + return dataQueue; +} +std::string AMQT::getQueueFrontPop() { + std::string value = std::move(dataQueue.front()); + dataQueue.pop(); + return value; +} +void AMQT::sendToTx(const std::string data) { + if (s_tx) { + (void)s_tx->trySend(std::move(data)); + } +} diff --git a/src/network/AMQClient.hpp b/src/network/AMQClient.hpp new file mode 100644 index 0000000..415bbf0 --- /dev/null +++ b/src/network/AMQClient.hpp @@ -0,0 +1,55 @@ +#pragma once +#include +#ifdef GEODE_IS_WINDOWS // stop the dumb compiler errors! +#include +#include +//#include +#endif +#include +#include +#include +#include +#include + +class AMQT { + private: + amqp_connection_state_t m_connection; + amqp_socket_t* m_socket; + std::string client_id; + + std::atomic m_remainingAttempts = {MAX_RECONNECT_ATTEMPTS}; + std::atomic m_connected{false}; + + std::mutex m_mutex; + std::condition_variable m_cv; + + std::chrono::steady_clock::time_point m_lastActivityTime; + std::chrono::steady_clock::time_point m_lastHeartbeatTime; + + public: + AMQT(const std::string& client_id) : client_id(std::move(client_id)), m_lastActivityTime(std::chrono::steady_clock::now()), m_lastHeartbeatTime(m_lastActivityTime) {}; + void updateActivity() { + m_lastActivityTime = std::chrono::steady_clock::now(); + } + // since the current heartbeat does NOT work!! + bool sendHeartbeat(); + bool isConnected() { + return m_connected.load(); + } + void setupChannel(); + void connect(); + void disconnect() { + if (m_connection) { + m_connected = false; + amqp_destroy_connection(m_connection); + m_connection = nullptr; + } + } + static bool genConnected(); + static void setGenConnected(bool value); + static std::queue getQueue(); + static std::string getQueueFrontPop(); + static void sendToTx(const std::string); + static void setTx(arc::mpsc::Sender tx); +}; + diff --git a/src/ui/DownloadPopup.cpp b/src/ui/DownloadPopup.cpp new file mode 100644 index 0000000..5d24fcb --- /dev/null +++ b/src/ui/DownloadPopup.cpp @@ -0,0 +1,81 @@ +#include "DownloadPopup.hpp" +#ifndef GEODE_IS_MACOS // unfortunately figs video player not supported on mac becuase opengl32 doesnt want to compile correctly koeirjnfioewbnfejwf +#include +#include +#include +#include + +// ------ +// 204 = MAX +// 0 = MIN +bool RobProgressBar::init() { // on purpose im not even providing any params because why would we!?; + progressBar->setPosition({3, 8}); + progressBar->setAnchorPoint({ 0, 0.5 }); + progressBar->setTextureRect(CCRect(0, 0, 204, 8)); + ccTexParams params = { GL_LINEAR, GL_LINEAR, GL_REPEAT, GL_REPEAT }; // tf is this nonsense mat + progressBar->getTexture()->setTexParameters(¶ms); + this->addChild(outerBg); + progressBar->setZOrder(-1); + outerBg->addChild(progressBar); + return true; +} + +void RobProgressBar::setProgress(float percentage) { + this->m_pPercentage = percentage; + float realPercentage = (204/100) * this->m_pPercentage; // "Result of integer division used in a floating point" ok i know clangd warnings wow + progressBar->setTextureRect(CCRect(0, 0, realPercentage, 8)); +} + +RobProgressBar* RobProgressBar::create() { + auto pRet = new RobProgressBar(); + if (pRet && pRet->init()) { + pRet->autorelease(); + return pRet; + } + CC_SAFE_DELETE(pRet); + return nullptr; +} + +bool DownloadPopup::init(const char* url, std::filesystem::path destination, std::function callback) { + if (!Popup::init(300.f, 150.f)) return false; + Build::create("Downloading...", "bigFont.fnt").scale(0.8f).parentAtPos(m_mainLayer, Anchor::Top, {0, -25}); + + loading_circle->setParentLayer(this); + loading_circle->setScale(0.675f); + loading_circle->show(); + m_progressBar = RobProgressBar::create(); + m_progressBar->setProgress(0.0F); + m_mainLayer->addChildAtPosition(m_progressBar, Anchor::Center, {0, -30}); + + Build::create("Do not close this menu while the file is being downloaded.", "chatFont.fnt").scale(.5f).alignment(cocos2d::kCCTextAlignmentCenter).parentAtPos(m_mainLayer, Anchor::Bottom, {0, 18}); + geode::utils::web::WebRequest req = web::WebRequest(); + req.onProgress([this](web::WebProgress const& progress) { + if (!progress.downloadProgress().has_value()) return; + log::debug("Progress({},{},{})", progress.downloadProgress(), progress.downloaded(), progress.downloadTotal()); + auto scene = CCDirector::sharedDirector()->getRunningScene(); + if (m_progressBar != nullptr) { + m_progressBar->setProgress(progress.downloadProgress().value()); + } + }); + m_listener.spawn( + req.get(url), + [this, destination, callback](web::WebResponse value) { + (void)geode::utils::file::writeBinarySafe(destination, value.data()); + onClose(nullptr); + callback(); + } + ); + return true; +} + +DownloadPopup* DownloadPopup::create(const char* url, std::filesystem::path destination, std::function callback) { + auto ret = new DownloadPopup(); + if (ret && ret->init(url, destination, callback)) { + ret->autorelease(); + return ret; + } + delete ret; + return nullptr; +} +// below is copied from the gdr mod lol +#endif diff --git a/src/ui/DownloadPopup.hpp b/src/ui/DownloadPopup.hpp new file mode 100644 index 0000000..b268bdb --- /dev/null +++ b/src/ui/DownloadPopup.hpp @@ -0,0 +1,30 @@ +#pragma once +// windows only because i cant get this to work, actually im too lazy +#ifndef GEODE_IS_MACOS +#include +using namespace geode::prelude; +class RobProgressBar : public CCNode { + protected: + float m_pPercentage = 0; + CCSprite* outerBg = CCSprite::create("slidergroove.png"); + CCSprite* progressBar = CCSprite::create("sliderBar.png"); + bool init(); + public: + void testProgressbar(); + virtual void setProgress(float percentage); + static RobProgressBar* create(); +}; +class DownloadPopup : public geode::Popup { + protected: + bool init(const char* url, std::filesystem::path, std::function); + RobProgressBar* m_progressBar; + LoadingCircle* loading_circle = LoadingCircle::create(); + async::TaskHolder m_listener; + public: + static DownloadPopup* create(const char* url, std::filesystem::path, std::function); //, cocos2d::SEL_MenuHandler callback + virtual void onClose(CCObject*) override { + m_listener.cancel(); + Popup::onClose(nullptr); + } +}; +#endif diff --git a/src/Settings/CreditsBetaMenu.cpp b/src/ui/credits/CreditsBetaMenu.cpp similarity index 96% rename from src/Settings/CreditsBetaMenu.cpp rename to src/ui/credits/CreditsBetaMenu.cpp index c62c6f3..6c65d14 100644 --- a/src/Settings/CreditsBetaMenu.cpp +++ b/src/ui/credits/CreditsBetaMenu.cpp @@ -1,5 +1,6 @@ -#include "CreditsBetaMenu.h" -#include "../includes.h" +#include "CreditsBetaMenu.hpp" +#include +using namespace geode::prelude; struct CreditData { std::string username; @@ -42,7 +43,8 @@ std::vector users = { auto paginated = misc::paginate(users, 6); -bool CreditsBetaMenu::setup() { +bool CreditsBetaMenu::init() { + if (!Popup::init(300.f, 200.f)) return false; auto winSize = cocos2d::CCDirector::sharedDirector()->getWinSize(); auto title = CCLabelBMFont::create("Beta Credits", "bigFont.fnt"); diff --git a/src/Settings/CreditsBetaMenu.h b/src/ui/credits/CreditsBetaMenu.hpp similarity index 74% rename from src/Settings/CreditsBetaMenu.h rename to src/ui/credits/CreditsBetaMenu.hpp index 16f5b7c..1883611 100644 --- a/src/Settings/CreditsBetaMenu.h +++ b/src/ui/credits/CreditsBetaMenu.hpp @@ -1,14 +1,14 @@ #pragma once -class CreditsBetaMenu : public geode::Popup<> { +class CreditsBetaMenu : public geode::Popup { protected: cocos2d::CCNode* m_section; uint8_t m_currentPage = 0; - bool setup() override; + bool init(); public: void updatePage(); static CreditsBetaMenu* create() { auto ret = new CreditsBetaMenu(); - if (ret->initAnchored(300.f, 200.f)) { + if (ret->init()) { ret->autorelease(); return ret; } diff --git a/src/Settings/CreditsMenu.cpp b/src/ui/credits/CreditsMenu.cpp similarity index 96% rename from src/Settings/CreditsMenu.cpp rename to src/ui/credits/CreditsMenu.cpp index 3db0ca1..5d7b38c 100644 --- a/src/Settings/CreditsMenu.cpp +++ b/src/ui/credits/CreditsMenu.cpp @@ -1,8 +1,8 @@ -#include "CreditsMenu.h" -#include "Geode/utils/web.hpp" +#include "CreditsMenu.hpp" using namespace geode::prelude; -bool CreditsMenu::setup() { +bool CreditsMenu::init() { + if (!Popup::init(300.f, 200.f)) return false; auto title = CCLabelBMFont::create("Credits", "bigFont.fnt"); title->setScale(0.8f); m_mainLayer->addChildAtPosition(title, Anchor::Top, {0, -25}); diff --git a/src/Settings/CreditsMenu.h b/src/ui/credits/CreditsMenu.hpp similarity index 67% rename from src/Settings/CreditsMenu.h rename to src/ui/credits/CreditsMenu.hpp index 03de185..0c9c48b 100644 --- a/src/Settings/CreditsMenu.h +++ b/src/ui/credits/CreditsMenu.hpp @@ -1,11 +1,11 @@ #pragma once -class CreditsMenu : public geode::Popup<> { +class CreditsMenu : public geode::Popup { protected: - bool setup() override; + bool init(); public: static CreditsMenu* create() { auto ret = new CreditsMenu(); - if (ret->initAnchored(300.f, 200.f)) { + if (ret->init()) { ret->autorelease(); return ret; } From 0215cea2c154061b6510f93045d184c53fc85940 Mon Sep 17 00:00:00 2001 From: FireMario211 <17692105+FireMario211@users.noreply.github.com> Date: Sun, 8 Feb 2026 16:23:00 -0500 Subject: [PATCH 02/32] feat: daily chests --- README.md | 25 ++- include/RateEvent.hpp | 3 + src/defs/misc.hpp | 17 -- src/hooks/MenuLayer.cpp | 97 ---------- src/hooks/SecretLayer2.cpp | 1 - src/main.cpp | 99 +++++----- src/modules/events/DailyChest.cpp | 270 ++++++++++++++++++++++++++++ src/modules/events/DailyChest.hpp | 12 ++ src/modules/events/EventHandler.cpp | 81 --------- src/modules/events/EventHandler.hpp | 1 - src/network/AMQClient.cpp | 7 +- test/mod.json | 20 +-- test/src/main.cpp | 18 +- 13 files changed, 388 insertions(+), 263 deletions(-) delete mode 100644 src/hooks/MenuLayer.cpp create mode 100644 src/modules/events/DailyChest.cpp create mode 100644 src/modules/events/DailyChest.hpp diff --git a/README.md b/README.md index 2dd1a48..02349f7 100644 --- a/README.md +++ b/README.md @@ -92,11 +92,28 @@ EventData data = { GDUtils::Events::RateEvent::emit(data); ``` -### Example Code (Listening for notification events from GDUtils) +### Example Code (Listening for notification events from GDUtils, and checking if the server is online) ```cpp $execute { - new EventListener>(+[](GDUtils::Events::OnRate* e) { - log::info("A rate event with the title {}", e->getTitle()); // A rate event with the title Small Daily Chest available! + // Listening for *any* rate event. + GDUtils::Events::OnRate().listen([](EventData const& e) { + log::info("A rate event with the title {}", e.getTitle()); // A rate event with the title Small Daily Chest available! + return ListenerResult::Propagate; + }); + + // Listening for rate events, filtering only by daily + GDUtils::Events::OnRate(EventType::Daily).listen([](EventData const& e) { + log::info("A daily event with the level name {}", e.getLevelName()); // A daily event with the level name Cycles + return ListenerResult::Propagate; + }); + + // Checking when we connected. + GDUtils::Events::OnServerConnect().listen([](bool const& connected) { + Loader::get()->queueInMainThread([connected]() { + if (connected) { + log::info("I am connected to the notifiaction servers!"); + } + }); return ListenerResult::Propagate; }); } @@ -105,7 +122,7 @@ View the `test` directory if you want to view an example mod using the API. ## Libraries Used - [Geode](https://github.com/geode-sdk/geode) -- [socket.io-client-cpp](https://github.com/socketio/socket.io-client-cpp) +- [rabbitmq-c](https://github.com/alanxz/rabbitmq-c) - [pl_mpeg](https://github.com/phoboslab/pl_mpeg) ## Credits diff --git a/include/RateEvent.hpp b/include/RateEvent.hpp index 4acfb9b..da14fbd 100644 --- a/include/RateEvent.hpp +++ b/include/RateEvent.hpp @@ -58,4 +58,7 @@ namespace GDUtils::Events { struct OnRate final : geode::GlobalEvent { using GlobalEvent::GlobalEvent; }; + struct OnServerConnect final : geode::ThreadSafeEvent { + using ThreadSafeEvent::ThreadSafeEvent; + }; } diff --git a/src/defs/misc.hpp b/src/defs/misc.hpp index 4cfc11c..2368b08 100644 --- a/src/defs/misc.hpp +++ b/src/defs/misc.hpp @@ -4,23 +4,6 @@ #include "geode.hpp" namespace misc { - // theres no including Geode Util class funcs so, https://github.com/geode-sdk/DevTools - /*static - static CCObject* findNode(std::string name) { - auto scene = CCDirector::sharedDirector()->getRunningScene(); - auto sceneChildren = scene->getChildren(); - for (unsigned int i = 0; i < scene->getChildrenCount(); i++) { - auto node = sceneChildren->objectAtIndex(i); - if (node != nullptr) { - std::string layerName = misc::getNodeName(node); - std::cout << layerName << std::endl; - if (layerName == name) { - return node; - } - } - } - return nullptr; - }*/ template std::vector> paginate(const std::vector& arr, size_t size) { std::vector> paginated; diff --git a/src/hooks/MenuLayer.cpp b/src/hooks/MenuLayer.cpp deleted file mode 100644 index 143bb62..0000000 --- a/src/hooks/MenuLayer.cpp +++ /dev/null @@ -1,97 +0,0 @@ -#include -#include -#include -#include -#include - -bool is_socketserver_started = false; -class $modify(MenuLayer) { - bool init() { - if (!MenuLayer::init()) return false; - return true; - - /*if (!is_dailychest_ready) { - std::thread hThread(dailyChestThread); - hThread.detach(); - is_dailychest_ready = true; - }*/ - if (!is_socketserver_started) { - bool startSocketServer = Mod::get()->template getSettingValue("socketServer"); - if (startSocketServer) { - if (!Mod::get()->hasSavedValue("clientId")) { - // this is just for statistics ok - #ifdef GEODE_IS_WINDOWS - std::string platform = "Windows"; - #elif defined(GEODE_IS_ANDROID) - std::string platform = "Android"; - #elif defined(GEODE_IS_MACOS) - std::string platform = "Mac"; - #elif defined(GEODE_IS_IOS) - std::string platform = "iOS"; - #else - std::string platform = "Unknown"; - #endif - - const std::string charset = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; - std::random_device rd; - std::mt19937 gen(rd()); - std::uniform_int_distribution<> dist(0, charset.size() - 1); - - std::string randomStr; - for (size_t i = 0; i < 8; ++i) { - randomStr.push_back(charset[dist(gen)]); - } - auto now = std::chrono::system_clock::now(); - auto now_ms = std::chrono::duration_cast(now.time_since_epoch()).count(); - int accountID = 0; - if (auto gjam = GJAccountManager::get()) { - accountID = gjam->m_accountID; - } - Mod::get()->setSavedValue("clientId", fmt::format("gdutils-{}-{}-{}-{}", accountID, platform, now_ms, randomStr)); - } - if (!Mod::get()->hasSavedValue("clientId")) { - Notification::create("Couldn't connect to server, invalid Client ID", NotificationIcon::Error)->show(); - return true; - } - /*auto [tx, rx] = arc::mpsc::channel(1024); // i mean why would robtop ever rate that many levels - async::runtime().spawn([rx = std::move(rx)]() mutable -> arc::Future<> { - while (true) { - auto opt = co_await rx.recv(); - if (!opt) { - co_return; - } - std::string msg = std::move(opt).unwrap(); - - Loader::get()->queueInMainThread([m = std::move(msg)]() mutable { - auto director = CCDirector::sharedDirector(); - if (!director) return; - auto scene = CCDirector::sharedDirector()->getRunningScene(); - if (!scene) return; - if (scene->getChildrenCount() == 0) return; - EventHandler::aaa(); - }); - } - co_return; - }); - auto director = CCDirector::sharedDirector(); - - if (director) { - auto scene = CCDirector::sharedDirector()->getRunningScene(); - if (scene && scene->getChildrenCount() > 0) { - //arc::spawn(EventHandler::consumer(std::move(rx), scene)); - } - }*/ - //arc::spawn(EventHandler::eventLoop(scene, std::move(rx))); - //[tx = std::move(tx) - std::thread hThread([]() mutable { - AMQT handler(Mod::get()->getSavedValue("clientId")); - handler.connect(); - }); - hThread.detach(); - } - is_socketserver_started = true; - } - return true; - } -}; - diff --git a/src/hooks/SecretLayer2.cpp b/src/hooks/SecretLayer2.cpp index b199779..0d646a5 100644 --- a/src/hooks/SecretLayer2.cpp +++ b/src/hooks/SecretLayer2.cpp @@ -34,7 +34,6 @@ class $modify(SecretVault, SecretLayer2) { if (!vault_text) { vault_text = this->getChildByType(1); } - log::debug("what are you {}", input); if (!strcmp(input.c_str(), "bad apple")) { if (m_fields->m_currentState != States::BadApple) { m_fields->m_currentState = States::BadApple; diff --git a/src/main.cpp b/src/main.cpp index 5274eda..cc09cd7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2,11 +2,8 @@ // Ported with help by Firee // Mod made by Jouca & Firee +#include "modules/events/DailyChest.hpp" $execute { - // Loader::get()->queueInMainThread([]{ - // CCScheduler::get()->scheduleSelector(schedule_selector(EventHandler::check), EventHandler::create(), 1.0F, false); - // //CCScheduler::get()->scheduleUpdateForTarget(EventHandler::create(), Priority::Last, false); - // }); // listenForSettingChanges("activate-background", [](bool value) { // log::info("Activate Background changed to {}", value); // toggleSpriteHooks(value); @@ -24,6 +21,60 @@ bool g_socket_server_ran = false; +$execute { + if (Mod::get()->getSettingValue("server-notification")) { + GDUtils::Events::OnServerConnect().listen([](bool const& connected) { + Loader::get()->queueInMainThread([connected]() { + if (connected) { + Notification::create("Connected to Rate Server!", NotificationIcon::Success)->show(); + } + }); + return ListenerResult::Stop; + }).leak(); + } + auto [tx, rx] = arc::mpsc::channel(1024); // i mean why would robtop ever rate that many levels + auto [etx, erx] = arc::mpsc::channel(1024); // ditto + auto [ctx, crx] = arc::mpsc::channel(64); + AMQT::setTx(std::move(tx)); + EventsPush::setTx(std::move(ctx)); + //auto interval = arc::interval(Duration::fromMillis(250)); + async::runtime().spawn([rx = std::move(rx), crx = std::move(crx), erx = std::move(erx)]() mutable -> arc::Future<> { + bool running = true; + while (running) { + co_await arc::select( + arc::selectee(rx.recv(), [&](auto req) { + if (req) { + std::string msg = std::move(req).unwrap(); + Loader::get()->queueInMainThread([m = std::move(msg)]() mutable { + auto director = CCDirector::sharedDirector(); + if (!director) return; + auto scene = CCDirector::sharedDirector()->getRunningScene(); + if (!scene) return; + if (scene->getChildrenCount() == 0) return; + if (EventsPush::canSendEvent(scene)) { + EventsPush::pushRateLevel(scene, m); + } + }); + } + }), + arc::selectee(crx.recv(), [&](auto req) { + if (req) { + bool b = std::move(req).unwrap(); + log::debug("Cancelled (Code = {})", b); + } else { + log::debug("Cancelled (Code = Unknown)"); + } + Loader::get()->queueInMainThread([]() { + if (auto scene = CCScene::get()) { + EventsPush::eventCompletedCallback(scene); + } + }); + }) + ); + } + }); +} + $on_game(Loaded) { if (g_socket_server_ran) return; if (geode::Mod::get()->getSettingValue("socketServer")) { @@ -61,46 +112,6 @@ bool g_socket_server_ran = false; Notification::create("Couldn't connect to server, invalid Client ID", NotificationIcon::Error)->show(); return; } - auto [tx, rx] = arc::mpsc::channel(1024); // i mean why would robtop ever rate that many levels - auto [etx, erx] = arc::mpsc::channel(1024); // ditto - auto [ctx, crx] = arc::mpsc::channel(64); - AMQT::setTx(std::move(tx)); - EventsPush::setTx(std::move(ctx)); - async::runtime().spawn([rx = std::move(rx), crx = std::move(crx), erx = std::move(erx)]() mutable -> arc::Future<> { - bool running = true; - while (running) { - co_await arc::select( - arc::selectee(rx.recv(), [&](auto req) { - if (req) { - std::string msg = std::move(req).unwrap(); - Loader::get()->queueInMainThread([m = std::move(msg)]() mutable { - auto director = CCDirector::sharedDirector(); - if (!director) return; - auto scene = CCDirector::sharedDirector()->getRunningScene(); - if (!scene) return; - if (scene->getChildrenCount() == 0) return; - if (EventsPush::canSendEvent(scene)) { - EventsPush::pushRateLevel(scene, m); - } - }); - } - }), - arc::selectee(crx.recv(), [&](auto req) { - if (req) { - bool b = std::move(req).unwrap(); - log::debug("Cancelled (Code = {})", b); - } else { - log::debug("Cancelled (Code = Unknown)"); - } - Loader::get()->queueInMainThread([]() { - if (auto scene = CCScene::get()) { - EventsPush::eventCompletedCallback(scene); - } - }); - }) - ); - } - }); std::thread hThread([]() mutable { AMQT handler(Mod::get()->getSavedValue("clientId")); handler.connect(); diff --git a/src/modules/events/DailyChest.cpp b/src/modules/events/DailyChest.cpp new file mode 100644 index 0000000..3a93930 --- /dev/null +++ b/src/modules/events/DailyChest.cpp @@ -0,0 +1,270 @@ +#include "DailyChest.hpp" +#include +#include "../include/RateEvent.hpp" +using namespace asp::time; + +#if defined(GEODE_IS_WINDOWS) +#include +#include +#else +#include +#endif +inline double rob_seconds(int remaining) { + if (remaining == 0) return 0; +#ifdef GEODE_IS_WINDOWS + __timeb64 current; + _ftime64_s(¤t); + // nice robert, i dunno if the bitmasking was intentional + auto result = (((double)((current.time & 0xFFFFF) * 1000 + current.millitm)) / 1000.0); +#else + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + auto result = ((double)ts.tv_sec + (double)ts.tv_nsec / 1000000000.0); +#endif + if (remaining < 0) return result; + return remaining - result; +} +inline double rob_seconds() { + return rob_seconds(-1); +} +enum class ChestKind { + Small, + Large +}; +struct ChestMsg { + enum class Type { + Refresh, + Opened, + Stop + } type; + + uint32_t smallTime = 0; + uint32_t largeTime = 0; + ChestKind opened{}; +}; +static GJRewardItem* time1 = nullptr; +static GJRewardItem* time2 = nullptr; +static std::optional> g_chestTx; + + +void DailyChest::getRewards() { + auto glm = GameLevelManager::sharedState(); + if (glm) { + glm->m_GJRewardDelegate = this; + glm->getGJRewards(0); + } +}; + +#include +void DailyChest::rewardsStatusFinished(int p0) { + GameStatsManager* gsm; + // Check if rewards are loaded + gsm = GameStatsManager::sharedState(); + if (gsm->m_rewardItems->count() == 0) { + log::debug("[DailyChest] no reward items, aborting"); + return; + } + + // Get the rewards + time1 = typeinfo_cast(gsm->m_rewardItems->objectForKey(0x1)); + time2 = typeinfo_cast(gsm->m_rewardItems->objectForKey(0x2)); + + uint32_t t1 = 0; + uint32_t t2 = 0; + if (time1) { + t1 = rob_seconds(time1->m_timeRemaining); + } + if (time2) { + t2 = rob_seconds(time2->m_timeRemaining); + } + + log::debug("[DailyChest] parsed timers: small={}s large={}s", t1, t2); + + if (g_chestTx) { + auto data = ChestMsg { + .type = ChestMsg::Type::Refresh, + .smallTime = t1, + .largeTime = t2 + }; + (void)g_chestTx->trySend(std::move(data)); + } +}; + +void DailyChest::rewardsStatusFailed() { + log::error("[DailyChest] Failed to get rewards"); +}; + +static void refreshRewards() { + auto dailyChest = new DailyChest(); + dailyChest->getRewards(); + + uint32_t t1 = 0; + uint32_t t2 = 0; + if (time1) t1 = rob_seconds(time1->m_timeRemaining); + if (time2) t2 = rob_seconds(time2->m_timeRemaining); +} + +void giveChestNotify(bool large) { + Loader::get()->queueInMainThread([large]() { + EventData data; + data.sprite = "GJ_square04.png"; + data.level_name = " "; + data.level_creator = " "; + if (large) { + data.type = EventType::largeChest; + data.title = "Big Daily Chest available!"; + } else { + data.type = EventType::smallChest; + data.title = "Small Daily Chest available!"; + } + GDUtils::Events::RateEvent::emit(data); + }); +} + +$on_game(Loaded) { + if (!Mod::get()->getSettingValue("largeChest") && !Mod::get()->getSettingValue("smallChest")) return; + auto [tx, rx] = arc::mpsc::channel(); + g_chestTx = std::move(tx); + // wait 5 seconds before + async::runtime().spawn([rx = std::move(rx)]() mutable -> arc::Future<> { + uint32_t smallRemaining = 0; + uint32_t largeRemaining = 0; + bool smallAwait = false; + bool largeAwait = false; + bool smallNotified = false; + bool largeNotified = false; + arc::CancellationToken cancel; + bool running = true; + + while (running) { + uint32_t nextWake = UINT32_MAX; + if (smallAwait) nextWake = std::min(nextWake, smallRemaining); + if (largeAwait) nextWake = std::min(nextWake, largeRemaining); + + log::debug( + "[DailyChest] loop: small={} ({}s), large={} ({}s)", + smallAwait, smallRemaining, + largeAwait, largeRemaining + ); + + co_await arc::select( + arc::selectee( + cancel.waitCancelled(), + [&] { running = false; } + ), + arc::selectee( + (nextWake != UINT32_MAX) + ? arc::sleepFor(Duration::fromSecs(nextWake)) + : arc::sleepFor(Duration::fromSecs(30)), + [&] { + log::debug("[DailyChest] sleep -> called! (after {}s)", nextWake); + if (smallAwait && smallRemaining == nextWake) { + log::debug("[DailyChest] sleep -> small chest notify"); + smallAwait = false; + smallNotified = true; + giveChestNotify(false); + } + if (largeAwait && largeRemaining == nextWake) { + log::debug("[DailyChest] sleep -> large chest notify"); + largeAwait = false; + largeNotified = true; + giveChestNotify(true); + } + } + ), + arc::selectee(rx.recv(), [&](auto req) { + if (req) { + log::debug("[DailyChest] rx -> called!"); + ChestMsg msg = std::move(req).unwrap(); + switch (msg.type) { + case ChestMsg::Type::Opened: + log::debug("[DailyChest] rx -> chest opened: {}", msg.opened == ChestKind::Small ? "small" : "large"); + // smallRemaining = msg.smallTime; + // largeRemaining = msg.largeTime; + // smallAwait = smallRemaining > 0; + // largeAwait = largeRemaining > 0; + if (msg.opened == ChestKind::Small) { + smallAwait = false; + smallNotified = false; + } else { + largeAwait = false; + largeNotified = false; + } + break; + case ChestMsg::Type::Refresh: + log::debug( + "[DailyChest] rx -> refresh: small={}s large={}s", + msg.smallTime, msg.largeTime + ); + if (msg.smallTime == 0) { + if (!smallAwait && !smallNotified) { + log::debug("[DailyChest] rx -> small chest notify"); + giveChestNotify(false); + } + smallAwait = false; + smallNotified = true; + smallRemaining = 0; + } else { + smallRemaining = msg.smallTime; + smallAwait = true; + } + if (msg.largeTime == 0) { + if (!largeAwait && !largeNotified) { + log::debug("[DailyChest] rx -> large chest notify"); + giveChestNotify(true); + } + largeAwait = false; + largeNotified = true; + largeRemaining = 0; + } else { + largeRemaining = msg.largeTime; + largeAwait = true; + } + break; + case ChestMsg::Type::Stop: + log::debug("[DailyChest] rx -> stop"); + cancel.cancel(); + return; + } + } + }) + ); + } + }); + listenForSettingChanges("smallChest", [](bool value) { + if (!value && !Mod::get()->getSettingValue("largeChest")) { + if (g_chestTx) { + (void)g_chestTx->trySend({ ChestMsg::Type::Stop }); + } + } + }); + listenForSettingChanges("largeChest", [](bool value) { + if (!value && !Mod::get()->getSettingValue("smallChest")) { + if (g_chestTx) { + (void)g_chestTx->trySend({ ChestMsg::Type::Stop }); + } + } + }); + refreshRewards(); +} +#include +class $modify(RULHook, RewardUnlockLayer) { + void step3() { + int tag = m_chestType; + if (g_chestTx) { + if (tag == 1) { + (void)g_chestTx->trySend({ + .type = ChestMsg::Type::Opened, + .opened = ChestKind::Small + }); + } else if (tag == 2) { + (void)g_chestTx->trySend({ + .type = ChestMsg::Type::Opened, + .opened = ChestKind::Large + }); + } + } + refreshRewards(); + RewardUnlockLayer::step3(); + } +}; diff --git a/src/modules/events/DailyChest.hpp b/src/modules/events/DailyChest.hpp new file mode 100644 index 0000000..3e2cf05 --- /dev/null +++ b/src/modules/events/DailyChest.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include +using namespace geode::prelude; + +class DailyChest : public CCObject, public GJRewardDelegate { + public: + void getRewards(); + void rewardsStatusFinished(int p0) override; + void rewardsStatusFailed() override; + static void startGame(); +}; diff --git a/src/modules/events/EventHandler.cpp b/src/modules/events/EventHandler.cpp index cc6207a..bdf609c 100644 --- a/src/modules/events/EventHandler.cpp +++ b/src/modules/events/EventHandler.cpp @@ -2,31 +2,6 @@ #include "EventsPush.hpp" #include -std::string getNodeNameA(cocos2d::CCObject* node) { -#ifdef GEODE_IS_WINDOWS - return typeid(*node).name() + 6; -#else - { - std::string ret; - - int status = 0; - auto demangle = abi::__cxa_demangle(typeid(*node).name(), 0, 0, &status); - if (status == 0) { - ret = demangle; - } - free(demangle); - - return ret; - } -#endif -} - -void processEvent(CCScene* self) { - if (!AMQT::getQueue().empty()) { - EventsPush::pushRateLevel(self, AMQT::getQueueFrontPop()); - } -} - //TODO: void processChestEvent(CCScene* self) { /*if (!chestQueue.empty()) { @@ -36,59 +11,3 @@ void processChestEvent(CCScene* self) { dailyChest->getRewards(0); }*/ } - - -void EventHandler::checkForEvent() { - auto director = CCDirector::sharedDirector(); - if (!director) return; - auto scene = CCDirector::sharedDirector()->getRunningScene(); - if (!scene) return; - if (scene->getChildrenCount() == 0) return; - if (AMQT::genConnected()) { - AMQT::setGenConnected(false); - if (Mod::get()->template getSettingValue("server-notification")) { - Notification::create("Connected to Rate Server!", NotificationIcon::Success)->show(); - } - } - auto layer = scene->getChildren()->objectAtIndex(0); - std::string layerName = getNodeNameA(layer); - if (layerName == "cocos2d::CCLayerColor") return; - if (layerName == "LoadingLayer") return; - if (m_currentLayer != layerName) { - m_currentLayer = layerName; - EventsPush::eventCompletedCallback(scene); - bool everywhereElse = Mod::get()->template getSettingValue("everywhereElse"); - if ((layerName != "LevelEditorLayer" && layerName != "PlayLayer") && !everywhereElse) return; - if ((layerName != "LevelEditorLayer" && layerName != "PlayLayer") && everywhereElse) { - processChestEvent(scene); - processEvent(scene); - } - } - if (AMQT::getQueue().empty()) return; - bool everywhereElse = Mod::get()->getSettingValue("everywhereElse"); - bool inLevels = Mod::get()->getSettingValue("inLevels"); - bool inEditor = Mod::get()->getSettingValue("inEditor"); - bool inPlatformers = Mod::get()->getSettingValue("inPlatformers"); - bool pushEvent = true; - if (layerName == "PlayLayer") { - if (!inLevels) pushEvent = false; - } - if (layerName == "LevelEditorLayer" && !inEditor) { - pushEvent = false; - } - if ((layerName != "LevelEditorLayer" && layerName != "PlayLayer") && !everywhereElse) { - pushEvent = false; - } - if (pushEvent) processEvent(scene); -} - -void EventHandler::consumer(arc::mpsc::Receiver rx, CCScene* scene) { - -} - -$execute { - /*Loader::get()->queueInMainThread([]{ - CCScheduler::get()->scheduleSelector(schedule_selector(EventHandler::check), EventHandler::create(), 1.0F, false); - //CCScheduler::get()->scheduleUpdateForTarget(EventHandler::create(), Priority::Last, false); - });*/ -} diff --git a/src/modules/events/EventHandler.hpp b/src/modules/events/EventHandler.hpp index a2775c1..f79cb08 100644 --- a/src/modules/events/EventHandler.hpp +++ b/src/modules/events/EventHandler.hpp @@ -114,6 +114,5 @@ class EventHandler : public CCObject { checkForEvent(); //checkForFiles(); } - static void consumer(arc::mpsc::Receiver rx, CCScene* scene); }; diff --git a/src/network/AMQClient.cpp b/src/network/AMQClient.cpp index c555136..5ae23ba 100644 --- a/src/network/AMQClient.cpp +++ b/src/network/AMQClient.cpp @@ -1,4 +1,5 @@ #include "AMQClient.hpp" +#include "../../include/RateEvent.hpp" bool show_connected = false; std::queue dataQueue; @@ -147,7 +148,7 @@ void AMQT::setupChannel() { } if (res) { std::string data((char*)res->consumer_tag.bytes, res->consumer_tag.len); - show_connected = true; + GDUtils::Events::OnServerConnect().send(true); log::info("Subscribed to Rate Notifications! (Consumer Tag: {})", data); } } @@ -163,7 +164,6 @@ void AMQT::setupChannel() { log::error("Couldn't send heartbeat, disconnecting..."); break; } - if (forceReconnection) { auto now = std::chrono::steady_clock::now(); auto lastDuration = std::chrono::duration_cast(now - lastReconnectTime).count(); @@ -172,10 +172,8 @@ void AMQT::setupChannel() { break; } } - amqp_envelope_t envelope; amqp_maybe_release_buffers(m_connection); - auto ret = amqp_consume_message(m_connection, &envelope, &timeout, 0); if (AMQP_RESPONSE_NORMAL != ret.reply_type) { if (AMQP_RESPONSE_LIBRARY_EXCEPTION == ret.reply_type && AMQP_STATUS_HEARTBEAT_TIMEOUT == ret.library_error) { @@ -228,6 +226,7 @@ void AMQT::setupChannel() { amqp_destroy_envelope(&envelope); } } + GDUtils::Events::OnServerConnect().send(false); m_connected = false; m_cv.notify_all(); } diff --git a/test/mod.json b/test/mod.json index 2f1f80d..4261b25 100644 --- a/test/mod.json +++ b/test/mod.json @@ -1,21 +1,17 @@ { - "geode": "4.9.0", + "geode": "5.0.0", "gd": { - "win": "2.2074", - "android": "2.2074", - "mac": "2.2074", - "ios": "2.2074" + "win": "2.2081", + "android": "2.2081", + "mac": "2.2081", + "ios": "2.2081" }, "id": "gdutilsdevs.test", "name": "GDUtils Test Mod", "version": "v1.0.0", "developer": "Firee", "description": "", - "dependencies": [ - { - "id": "gdutilsdevs.gdutils", - "version": ">=v1.4.0-beta.6", - "importance": "required" - } - ] + "dependencies": { + "gdutilsdevs.gdutils": ">=v1.6.0" + } } diff --git a/test/src/main.cpp b/test/src/main.cpp index 3509891..25ad723 100644 --- a/test/src/main.cpp +++ b/test/src/main.cpp @@ -26,8 +26,22 @@ class $modify(MenuLayer) { }; $execute { - new EventListener>(+[](GDUtils::Events::OnRate* e) { - log::info("A rate event with the title {}", e->getTitle()); + GDUtils::Events::OnRate().listen([](EventData const& e) { + log::info("A rate event with the title {}", e.getTitle()); + return ListenerResult::Propagate; + }); + + GDUtils::Events::OnRate(EventType::Daily).listen([](EventData const& e) { + log::info("A daily event with the level name {}", e.getLevelName()); + return ListenerResult::Propagate; + }); + + GDUtils::Events::OnServerConnect().listen([](bool const& connected) { + Loader::get()->queueInMainThread([connected]() { + if (connected) { + log::info("I am connected to the notifiaction servers!"); + } + }); return ListenerResult::Propagate; }); } From 3736fb3cf69b2f6d527db72580f24abfbd323e75 Mon Sep 17 00:00:00 2001 From: FireMario211 <17692105+FireMario211@users.noreply.github.com> Date: Mon, 16 Feb 2026 14:00:09 -0500 Subject: [PATCH 03/32] add more to rewrite --- include/RateEvent.hpp | 4 +- resources/gauntlets/particles.json | 1154 ++++++++++++++++++++++++++ src/hooks/CustomBadges.cpp | 467 +++++++++++ src/hooks/DailyLevelPage.cpp | 40 + src/hooks/FriendsProfilePage.cpp | 153 ++++ src/hooks/GJGarageLayer.cpp | 74 ++ src/hooks/GauntletSelectLayer.cpp | 1232 ++++++++++++++++++++++++++++ src/hooks/InfoLayer.cpp | 45 + src/hooks/LevelInfoLayer.cpp | 4 +- src/hooks/SecretLayer.cpp | 44 + src/ui/DownloadPopup.cpp | 10 +- src/ui/EventInfoPopup.cpp | 69 ++ src/ui/EventInfoPopup.hpp | 22 + src/ui/SearchUserLayer.cpp | 14 + src/ui/SearchUserLayer.hpp | 22 + 15 files changed, 3345 insertions(+), 9 deletions(-) create mode 100644 resources/gauntlets/particles.json create mode 100644 src/hooks/CustomBadges.cpp create mode 100644 src/hooks/DailyLevelPage.cpp create mode 100644 src/hooks/FriendsProfilePage.cpp create mode 100644 src/hooks/GJGarageLayer.cpp create mode 100644 src/hooks/GauntletSelectLayer.cpp create mode 100644 src/hooks/InfoLayer.cpp create mode 100644 src/hooks/SecretLayer.cpp create mode 100644 src/ui/EventInfoPopup.cpp create mode 100644 src/ui/EventInfoPopup.hpp create mode 100644 src/ui/SearchUserLayer.cpp create mode 100644 src/ui/SearchUserLayer.hpp diff --git a/include/RateEvent.hpp b/include/RateEvent.hpp index da14fbd..6999984 100644 --- a/include/RateEvent.hpp +++ b/include/RateEvent.hpp @@ -4,9 +4,9 @@ #include "Types.hpp" namespace GDUtils::Events { - class RateEvent : public geode::SimpleEvent { + class RateEvent : public geode::Event { public: - using SimpleEvent::SimpleEvent; + using Event::Event; static void emit(EventData data) { RateEvent().send(data); }; diff --git a/resources/gauntlets/particles.json b/resources/gauntlets/particles.json new file mode 100644 index 0000000..c179550 --- /dev/null +++ b/resources/gauntlets/particles.json @@ -0,0 +1,1154 @@ +{ + "World": { + "background": { + "sprite": "world_gauntlet.png", + "scale": 1.27 + }, + "gauntlet": { + "position": [ + 1.0, + 16.0 + ], + "scale": 1.0 + }, + "particles": [ + { + "string": "22a-1a3a0a20a90a0a29a0a0a0a0a0a0a0a0a0a7a0a0a0a0.729412a0a0.352941a0a0.796078a0a1a0a7a0a0a0a0.729412a0a0.352941a0a0.796078a0a1a0a0.4a0a0.4a0a35a0a35a0a-50a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": [ + 10.0, + 24.0 + ], + "scale": [ + 1.31, + 2.61 + ] + }, + { + "string": "22a-1a3a0a20a90a0a29a0a0a0a0a0a0a0a0a0a7a0a0a0a0.34902a0a0.466667a0a0.792157a0a1a0a7a0a0a0a0.34902a0a0.466667a0a0.792157a0a1a0a0.4a0a0.4a0a35a0a35a0a-50a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": [ + 6.0, + 24.0 + ], + "scale": [ + 1.31, + 2.61 + ] + }, + { + "string": "22a-1a3a0a20a90a0a29a0a0a0a0a0a0a0a0a0a7a0a0a0a0.360784a0a0.854902a0a0.368627a0a1a0a7a0a0a0a0.360784a0a0.854902a0a0.368627a0a1a0a0.4a0a0.4a0a35a0a35a0a-50a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": [ + 2.0, + 24.0 + ], + "scale": [ + 1.31, + 2.61 + ] + }, + { + "string": "22a-1a3a0a20a90a0a29a0a0a0a0a0a0a0a0a0a7a0a0a0a0.866667a0a0.839216a0a0.247059a0a1a0a7a0a0a0a0.866667a0a0.839216a0a0.247059a0a1a0a0.4a0a0.4a0a35a0a35a0a-50a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": [ + -2.0, + 24.0 + ], + "scale": [ + 1.31, + 2.61 + ] + }, + { + "string": "22a-1a3a0a20a90a0a29a0a0a0a0a0a0a0a0a0a7a0a0a0a0.866667a0a0.294118a0a0.247059a0a1a0a7a0a0a0a0.866667a0a0.294118a0a0.247059a0a1a0a0.4a0a0.4a0a35a0a35a0a-50a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": [ + -6.0, + 24.0 + ], + "scale": [ + 1.31, + 2.61 + ] + }, + { + "string": "22a-1a3a0a20a90a0a29a0a0a0a0a0a0a0a0a0a7a0a0a0a0.729412a0a0.352941a0a0.796078a0a1a0a7a0a0a0a0.729412a0a0.352941a0a0.796078a0a1a0a0.4a0a0.4a0a35a0a35a0a-50a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": [ + -9.0, + 24.0 + ], + "scale": [ + -1.31, + 2.61 + ] + }, + { + "string": "22a-1a3a0a20a90a0a29a0a0a0a0a0a0a0a0a0a7a0a0a0a0.34902a0a0.466667a0a0.792157a0a1a0a7a0a0a0a0.34902a0a0.466667a0a0.792157a0a1a0a0.4a0a0.4a0a35a0a35a0a-50a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": [ + -5.0, + 24.0 + ], + "scale": [ + -1.31, + 2.61 + ] + }, + { + "string": "22a-1a3a0a20a90a0a29a0a0a0a0a0a0a0a0a0a7a0a0a0a0.360784a0a0.854902a0a0.368627a0a1a0a7a0a0a0a0.360784a0a0.854902a0a0.368627a0a1a0a0.4a0a0.4a0a35a0a35a0a-50a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": [ + -1.0, + 24.0 + ], + "scale": [ + -1.31, + 2.61 + ] + }, + { + "string": "22a-1a3a0a20a90a0a29a0a0a0a0a0a0a0a0a0a7a0a0a0a0.866667a0a0.839216a0a0.247059a0a1a0a7a0a0a0a0.866667a0a0.839216a0a0.247059a0a1a0a0.4a0a0.4a0a35a0a35a0a-50a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": [ + 3.0, + 24.0 + ], + "scale": [ + -1.31, + 2.61 + ] + }, + { + "string": "22a-1a3a0a20a90a0a29a0a0a0a0a0a0a0a0a0a7a0a0a0a0.866667a0a0.294118a0a0.247059a0a1a0a7a0a0a0a0.866667a0a0.294118a0a0.247059a0a1a0a0.4a0a0.4a0a35a0a35a0a-50a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": [ + 7.0, + 24.0 + ], + "scale": [ + -1.31, + 2.61 + ] + }, + { + "string": "38a-1a1.5a0.3a21a90a90a0a0a31a69a0a0a0a0a0a0a2a1a65a57a1a0a1a0a1a0a1a0a5a1a0a0a1a0a1a0a1a0a1a0a0.2a0a0.4a0a0a0a0a0a0a0a0a2a1a0a0a0a169a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": [ + 1.0, + 6.0 + ], + "scale": [ + 1.71, + 1.66 + ] + }, + { + "string": "11a-1a1.5a0.3a6a90a90a0a0a31a69a0a0a0a0a0a0a4a1a0a0a1a0a1a0a1a0a1a0a7a1a52a55a1a0a1a0a1a0a1a0a0.2a0a0.4a0a0a0a0a0a0a0a0a2a1a0a0a0a167a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": [ + 1.0, + 16.0 + ], + "scale": [ + 1.835, + 1.485 + ] + } + ] + }, + "Fantasy": { + "background": { + "sprite": "fantasy_gauntlet.png", + "scale": 1.225 + }, + "gauntlet": { + "position": [ + -2.0, + 14.0 + ], + "scale": 1.0 + }, + "particles": [ + { + "string": "26a-1a5a0.3a4a-90a14a9a0a17a0a0a0a0a0a0a0a2a1a76a325a0.537255a0a0.898039a0a0.164706a0a1a0a2a1a148a149a0.537255a0a0.898039a0a0.164706a0a1a0a0a0a0.3a0a0a0a0a0a0a0a0a2a0a0a0a0a146a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": null, + "scale": [ + 2.88, + 4.603 + ] + } + ] + }, + "Portal": { + "background": { + "sprite": "portal_gauntlet.png", + "scale": 1.225 + }, + "gauntlet": { + "position": [ + -2.0, + 14.0 + ], + "scale": 1.0 + }, + "particles": [ + { + "string": "13a-1a1a0.3a10a90a90a29a0a11a0a0a0a0a0a0a0a50a1a0a37a0.376471a0a0.109804a0a0.243137a0a1a0a32a1a142a96a0.376471a0a0.109804a0a0.243137a0a1a0a0.5a0a0.3a0a0a0a0a0a0a0a1a2a1a0a0a0a190a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": null, + "scale": [ + 2.88, + 3.978 + ] + }, + { + "string": "13a-1a1a0.3a10a90a90a29a0a11a0a0a0a0a0a0a0a50a1a0a37a0.0705882a0a0.172549a0a0.203922a0a1a0a32a1a142a96a0.0705882a0a0.172549a0a0.203922a0a1a0a0.5a0a0.3a0a0a0a0a0a0a0a1a2a1a0a0a0a190a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": null, + "scale": [ + 2.88, + 3.978 + ] + }, + { + "string": "38a-1a2a0.3a16a90a180a29a0a11a0a0a0a0a0a0a0a6a1a0a0a0.760784a0a0.380392a0a0.835294a0a1a0a3a1a0a0a0.760784a0a0.380392a0a0.835294a0a1a0a0.18a0a0.49a0a43a0a33a0a-34a0a1a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": null, + "scale": [ + 1.03, + 1.03 + ] + }, + { + "string": "38a-1a2a0.3a16a90a180a29a0a11a0a0a0a0a0a0a0a6a1a0a0a0.376471a0a0.690196a0a0.831373a0a1a0a3a1a0a0a0.376471a0a0.690196a0a0.831373a0a1a0a0.18a0a0.49a0a43a0a33a0a-34a0a1a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": null, + "scale": [ + 1.155, + 1.355 + ] + }, + { + "string": "30a-1a1a0.3a30a90a180a29a0a11a0a0a0a0a0a0a0a4a1a0a0a0.760784a0a0.380392a0a0.835294a0a1a0a2a1a0a0a0.760784a0a0.380392a0a0.835294a0a1a0a0.18a0a0.49a0a43a0a33a0a-41a0a1a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": null, + "scale": [ + 1.455, + 1.655 + ] + }, + { + "string": "30a-1a1a0.3a30a90a180a29a0a11a0a0a0a0a0a0a0a4a1a0a0a0.376471a0a0.690196a0a0.831373a0a1a0a2a1a0a0a0.376471a0a0.690196a0a0.831373a0a1a0a0.18a0a0.49a0a43a0a33a0a-41a0a1a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": null, + "scale": [ + 1.355, + 1.855 + ] + } + ] + }, + "Demon": { + "background": { + "sprite": "demon_gauntlet.png", + "scale": 1.275 + }, + "gauntlet": { + "position": [ + -1.0, + 22.0 + ], + "scale": 1.0 + }, + "particles": [ + { + "string": "66a-1a3a0.3a20a90a27a11a0a37a38a0a0a0a0a0a0a5a1a0a0a0.694118a0a0.45098a0a0.054902a0a1a0a2a1a0a0a0.509804a0a0.172549a0a0a0a1a0a0.5a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": null, + "scale": [ + 1.0, + 1.0 + ] + }, + { + "string": "29a-1a3a0.3a8a90a27a11a0a37a38a0a0a0a0a0a0a50a1a0a0a0.0509804a0a0.0392157a0a0.0352941a0a1a0a50a1a0a0a0.0509804a0a0.0392157a0a0.0352941a0a1a0a0.5a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": null, + "scale": [ + 1.0, + 1.0 + ] + }, + { + "string": "31a-1a1a0.3a23a90a90a29a0a11a0a0a0a0a0a0a0a33a1a89a149a0.454902a0a0.321569a0a0.152941a0a1a0a50a1a145a68a0.454902a0a0.321569a0a0.152941a0a1a0a0.5a0a0.5a0a0a0a0a0a0a0a1a2a1a0a0a0a192a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": null, + "scale": [ + 1.0, + 1.0 + ] + }, + { + "string": "30a-1a2a0.3a13a90a8a21a0a8a0a0a45a0a0a0a0a40a1a89a149a0.396078a0a0.27451a0a0.14902a0a1a0a24a1a145a68a0.141176a0a0.0352941a0a0.0313726a0a1a0a0.5a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": null, + "scale": [ + 1.0, + 1.0 + ] + }, + { + "string": "30a-1a1a0.3a13a90a8a21a0a0a0a0a45a0a0a0a0a21a1a89a149a0.894118a0a0.631373a0a0.345098a0a1a0a6a1a145a68a0.894118a0a0.631373a0a0.345098a0a1a0a0.5a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a183a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": null, + "scale": [ + 1.0, + 1.0 + ] + } + ] + }, + "Castle": { + "background": { + "sprite": "castle_gauntlet.png", + "scale": 1.275 + } + }, + "Galaxy": { + "background": { + "sprite": "galaxy_gauntlet.png", + "scale": 1.695 + }, + "gauntlet": { + "position": [ + 0.0, + 11.0 + ], + "scale": 0.975 + }, + "particles": [ + { + "string": "11a-1a1a0.3a8a90a90a0a0a0a0a0a0a0a0a0a0a50a3a0a0a0.627451a0a0.286275a0a0.470588a0a1a0a50a3a106a70a0.627451a0a0.286275a0a0.470588a0a1a0a0a0a0a0a0a0a0a0a0a0a0a2a1a0a0a0a176a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": [ + 0.0, + 24.0 + ], + "scale": [ + 1.61, + 1.61 + ] + }, + { + "string": "14a-1a1.2a0.3a9a112a6a42a0a15a0a0a0a0a0a0a0a4a0a340a0a0.913725a0a0.72549a0a0.337255a0a1a0a4a0a340a0a0.913725a0a0.72549a0a0.337255a0a1a0a0.3a0a0.3a0a0a0a0a0a0a0a0a2a1a0a0a0a31a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": [ + -25.0, + 111.0 + ], + "scale": [ + 2.66, + 3.71 + ] + }, + { + "string": "38a-1a1.5a0.3a21a90a90a0a0a31a69a0a0a0a0a0a0a2a1a65a57a0.886275a0a0.866667a0a0.454902a0a1a0a5a1a0a0a0.886275a0a0.866667a0a0.454902a0a1a0a0.2a0a0.4a0a0a0a0a0a0a0a0a2a1a0a0a0a169a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": [ + 2.0, + 6.0 + ], + "scale": [ + 1.685, + 1.485 + ] + }, + { + "string": "11a-1a1.5a0.3a6a90a90a0a0a31a69a0a0a0a0a0a0a4a1a0a0a0.890196a0a0.376471a0a0.65098a0a1a0a7a1a52a55a0.890196a0a0.376471a0a0.65098a0a1a0a0.2a0a0.4a0a0a0a0a0a0a0a0a2a1a0a0a0a167a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": [ + 2.0, + -2.0 + ], + "scale": [ + 1.31, + 1.635 + ] + }, + { + "string": "4a-1a1a0.3a3a90a90a0a0a0a0a0a0a0a0a0a0a50a3a0a0a0.862745a0a0.352941a0a0.623529a0a1a0a50a3a106a22a0.862745a0a0.352941a0a0.623529a0a1a0a0.3a0a0.3a0a0a0a0a0a0a0a0a2a1a0a0a0a164a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": [ + 0.0, + 24.0 + ], + "scale": [ + 1.785, + 1.785 + ] + } + ] + }, + "Universe": { + "background": { + "sprite": "universe_gauntlet.png", + "scale": 1.27 + }, + "gauntlet": { + "position": [ + 0.0, + 16.0 + ], + "scale": 1.0 + }, + "particles": [ + { + "string": "23a-1a3a0.3a6a90a180a29a0a11a0a0a0a0a0a0a0a38a1a0a0a0.0627451a0a0.0980392a0a0.105882a0a1a0a50a1a0a0a0.0627451a0a0.0980392a0a0.105882a0a1a0a0.1a0a0.5a0a10a0a28a0a0a0a1a2a1a0a1a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": [ + 0.0, + 11.0 + ], + "scale": [ + 1.485, + 2.685 + ] + }, + { + "string": "20a-1a0.5a0a15a90a0a44a0a11a0a0a0a0a0a0a0a4a1a0a0a0.760784a0a0.447059a0a0.94902a0a1a0a4a1a0a0a0.760784a0a0.447059a0a0.94902a0a1a0a0.1a0a0.1a0a0a0a0a0a0a0a0a2a1a0a0a0a33a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": [ + 32.0, + -74.0 + ], + "scale": [ + 3.685, + 8.31 + ] + }, + { + "string": "19a-1a0.5a0a38a90a0a44a0a20a0a0a0a0a0a0a0a2a1a0a0a0.94902a0a0.67451a0a0.447059a0a1a0a2a1a0a0a0.94902a0a0.67451a0a0.447059a0a1a0a0.1a0a0.1a0a0a0a0a0a0a0a0a2a1a0a0a0a33a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": [ + 34.0, + -96.0 + ], + "scale": [ + 2.135, + 8.935 + ] + }, + { + "string": "20a-1a1a0a15a90a0a36a0a11a0a0a0a0a0a0a0a4a1a0a0a0.447059a0a0.654902a0a0.94902a0a1a0a4a1a0a0a0.447059a0a0.654902a0a0.94902a0a1a0a0.2a0a0.2a0a0a0a0a0a0a0a0a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": [ + 41.0, + -92.0 + ], + "scale": [ + 3.235, + 6.585 + ] + }, + { + "string": "11a-1a1a0.3a8a90a90a0a0a0a0a0a0a0a0a0a0a50a3a0a0a0.239216a0a0.403922a0a0.419608a0a1a0a50a3a106a70a0.239216a0a0.403922a0a0.419608a0a1a0a0a0a0a0a0a0a0a0a0a0a0a2a1a0a0a0a176a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": [ + 0.0, + 24.0 + ], + "scale": [ + 1.71, + 1.71 + ] + }, + { + "string": "4a-1a1a0.3a3a90a90a0a0a0a0a0a0a0a0a0a0a50a3a0a0a0.32549a0a0.537255a0a0.560784a0a1a0a50a3a106a22a0.32549a0a0.537255a0a0.560784a0a1a0a0.3a0a0.3a0a0a0a0a0a0a0a0a2a1a0a0a0a190a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": [ + -2.0, + 24.0 + ], + "scale": [ + 2.71, + 2.71 + ] + } + ] + }, + "Cursed": { + "background": { + "sprite": "cursed_gauntlet.png", + "scale": 1.27 + }, + "gauntlet": { + "position": [ + 1.0, + 16.0 + ], + "scale": 1.0 + }, + "particles": [ + { + "string": "5a-1a1a0.3a3a90a90a29a0a11a0a0a0a0a0a0a0a50a1a24a45a0.105882a0a0.380392a0a0.105882a0a1a0a44a1a48a28a0.105882a0a0.380392a0a0.105882a0a1a0a0a0a0a0a0a0a0a0a0a0a1a2a1a0a0a0a175a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": null, + "scale": [ + 2.035, + 2.035 + ] + }, + { + "string": "5a-1a1a0.3a3a90a90a29a0a11a0a0a0a0a0a0a0a50a1a24a45a0.105882a0a0.380392a0a0.105882a0a1a0a44a1a48a28a0.105882a0a0.380392a0a0.105882a0a1a0a0a0a0a0a0a0a0a0a0a0a1a2a1a0a0a0a175a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": null, + "scale": [ + 2.035, + 2.035 + ] + }, + { + "string": "93a-1a1a0.3a71a90a0a52a0a10a0a23a0a0a0a0a0a2a1a0a0a0.00392157a0a0.905882a0a0a0a1a0a2a1a30a0a0.00392157a0a0.905882a0a0a0a1a0a0.3a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a32a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": [ + 17.0, + 94.0 + ], + "scale": [ + 4.51, + 3.335 + ] + } + ] + }, + "Doom": { + "background": { + "sprite": "doom_gauntlet.png", + "scale": 1.27 + }, + "gauntlet": { + "position": [ + 0.0, + 19.0 + ], + "scale": 1.0 + }, + "particles": [ + { + "string": "66a-1a3a0.3a20a90a27a11a0a43a83a0a0a0a0a0a0a5a1a0a0a0.733333a0a0.607843a0a0.12549a0a1a0a3a1a0a0a0.733333a0a0.470588a0a0.12549a0a1a0a0.5a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": [ + 0.0, + -7.0 + ], + "scale": [ + 1.235, + 1.235 + ] + }, + { + "string": "44a-1a3a0.3a13a90a27a11a0a32a62a0a0a0a0a0a0a50a1a0a0a0.0666667a0a0.0509804a0a0.0470588a0a1a0a50a1a0a0a0.0666667a0a0.0509804a0a0.0470588a0a1a0a0.5a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": [ + 0.0, + -13.0 + ], + "scale": [ + 1.31, + 1.435 + ] + }, + { + "string": "6a-1a1.9a0a3a90a17a69a0a3a0a0a-102a0a0a0a0a4a1a57a105a1a0a1a0a1a0a1a0a4a1a0a0a1a0a1a0a1a0a1a0a0a0a0a0a0a0a0a0a0a0a0a2a1a0a0a0a145a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": null, + "scale": [ + 2.035, + 2.035 + ] + } + ] + }, + "Split": { + "background": { + "sprite": "split_gauntlet.png", + "scale": 1.27 + }, + "gauntlet": { + "position": [ + -1.0, + 20.0 + ], + "scale": 1.0 + }, + "particles": [ + { + "string": "48a-1a2a0.3a20a90a2a19a0a10a20a0a0a0a0a0a0a4a1a0a0a0.666667a0a0.360784a0a0.603922a0a1a0a2a1a0a0a0.666667a0a0.360784a0a0.603922a0a1a0a0.5a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a31a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": [ + -28.0, + -65.0 + ], + "scale": [ + 2.18, + 4.003 + ] + }, + { + "string": "48a-1a2a0.3a20a90a2a19a0a10a20a0a0a0a0a0a0a4a1a0a0a0.454902a0a0.215686a0a0.403922a0a1a0a2a1a0a0a0.454902a0a0.215686a0a0.403922a0a1a0a0.5a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": [ + -28.0, + -65.0 + ], + "scale": [ + 2.18, + 4.003 + ] + }, + { + "string": "48a-1a2a0.3a20a90a2a19a0a10a20a0a0a0a0a0a0a4a1a0a0a0.243137a0a0.329412a0a0.341176a0a1a0a2a1a0a0a0.243137a0a0.329412a0a0.341176a0a1a0a0.5a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a31a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": [ + 25.0, + 65.0 + ], + "scale": [ + 2.18, + 4.003 + ] + }, + { + "string": "48a-1a2a0.3a20a90a2a19a0a10a20a0a0a0a0a0a0a4a1a0a0a0.145098a0a0.203922a0a0.207843a0a1a0a2a1a0a0a0.145098a0a0.203922a0a0.207843a0a1a0a0.5a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": [ + 25.0, + 65.0 + ], + "scale": [ + 2.18, + 4.003 + ] + }, + { + "string": "30a-1a2a0.3a13a90a90a29a0a11a0a0a0a0a0a0a0a6a1a0a0a0.705882a0a0.34902a0a0.698039a0a1a0a2a1a0a0a0.584314a0a0.34902a0a0.705882a0a1a0a0a0a0.5a0a6a0a34a0a0a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": [ + 0.0, + 29.0 + ], + "scale": [ + 1.955, + 1.955 + ] + }, + { + "string": "30a-1a2a0.3a13a90a90a29a0a11a0a0a0a0a0a0a0a6a1a0a0a0.254902a0a0.333333a0a0.388235a0a1a0a2a1a0a0a0.254902a0a0.333333a0a0.388235a0a1a0a0a0a0.5a0a6a0a34a0a0a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": [ + 0.0, + 29.0 + ], + "scale": [ + 1.955, + 1.955 + ] + }, + { + "string": "30a-1a2a0.3a13a90a90a29a0a11a0a0a0a0a0a0a0a6a1a0a0a0.705882a0a0.34902a0a0.698039a0a1a0a2a1a0a0a0.584314a0a0.34902a0a0.705882a0a1a0a0a0a0.5a0a6a0a34a0a0a0a1a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": [ + 0.0, + 29.0 + ], + "scale": [ + 1.955, + 1.955 + ] + }, + { + "string": "30a-1a2a0.3a13a90a90a29a0a11a0a0a0a0a0a0a0a6a1a0a0a0.254902a0a0.333333a0a0.388235a0a1a0a2a1a0a0a0.254902a0a0.333333a0a0.388235a0a1a0a0a0a0.5a0a6a0a34a0a0a0a1a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": [ + 0.0, + 29.0 + ], + "scale": [ + 1.955, + 1.955 + ] + } + ] + }, + "Monster": { + "background": { + "sprite": "monster_gauntlet.png", + "scale": 1.27 + }, + "gauntlet": { + "position": [ + 0.0, + 22.0 + ], + "scale": 1.0 + }, + "particles": [ + { + "string": "30a-1a3a0.3a9a90a9a11a0a17a40a0a0a0a0a0a0a4a1a0a0a0.0509804a0a0.929412a0a0a0a1a0a2a1a0a0a0.360784a0a0.486275a0a0.352941a0a1a0a0.1a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a165a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": null, + "scale": [ + 2.51, + 1.985 + ] + }, + { + "string": "66a-1a3a0.3a20a90a27a11a0a37a38a0a0a0a0a0a0a5a1a0a0a0.0156863a0a0.866667a0a0.027451a0a1a0a3a1a0a0a0.164706a0a0.509804a0a0a0a1a0a0.5a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": null, + "scale": [ + 1.072, + 2.475 + ] + }, + { + "string": "18a-1a3a0.3a5a90a9a11a0a32a28a0a0a0a0a0a0a50a1a0a0a0.0352941a0a0.0901961a0a0.0313726a0a1a0a50a1a0a0a0.0352941a0a0.0901961a0a0.0313726a0a1a0a1a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": null, + "scale": [ + 1.47, + 1.957 + ] + } + ] + }, + "Mystery": { + "background": { + "sprite": "mystery_gauntlet.png", + "scale": 1.27 + }, + "gauntlet": { + "position": [ + 3.0, + 13.0 + ], + "scale": 1.0 + }, + "particles": [ + { + "string": "30a-1a3a0.3a9a90a180a29a0a11a0a0a0a0a0a0a0a2a1a0a0a1a0a0.8a0a0.258824a0a1a0a8a1a0a0a1a0a0.8a0a0.258824a0a1a0a0.5a0a0.5a0a6a0a30a0a0a0a1a2a0a0a0a0a11a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": null, + "scale": [ + 2.035, + 2.035 + ] + }, + { + "string": "31a-1a3a0.3a9a90a180a0a0a29a82a0a0a0a0a0a0a4a1a0a0a0.388235a0a0.741176a0a1a0a1a0a15a1a0a0a0.388235a0a0.741176a0a1a0a1a0a1a0a1a0a6a0a30a0a0a0a0a2a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": [ + 0.0, + 0.0 + ], + "scale": [ + 1.435, + 1.435 + ] + } + ] + }, + "Strange": { + "background": { + "sprite": "strange_gauntlet.png", + "scale": 1.27 + }, + "gauntlet": { + "position": [ + 2.0, + -18.0 + ], + "scale": 1.0 + }, + "particles": [ + { + "string": "36a-1a3a0.3a6a-90a24a9a0a11a0a0a-7a0a0a0a0a2a1a76a325a0.592157a0a0.294118a0a0.658824a0a1a0a2a1a201a149a0.592157a0a0.294118a0a0.658824a0a1a0a0.3a0a0.3a0a0a0a0a0a0a0a0a2a0a0a0a0a96a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": [ + 2.0, + -65.0 + ], + "scale": [ + 2.48, + 2.728 + ] + }, + { + "string": "17a-1a4a0.3a3a180a0a8a0a47a34a0a0a0a0a0a0a50a1a0a0a0.243137a0a0.239216a0a0.239216a0a1a0a50a1a0a0a0.454902a0a0.447059a0a0.447059a0a1a0a1a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": [ + 17.0, + 60.0 + ], + "scale": [ + 0.805, + 1.155 + ] + } + ] + }, + "Water": { + "background": { + "sprite": "water_gauntlet.png", + "scale": 1.27 + } + }, + "Fire": { + "background": { + "sprite": "fire_gauntlet.png", + "scale": 1.27 + }, + "gauntlet": { + "position": [ + 0.0, + 15.0 + ], + "scale": 1.0 + }, + "particles": [ + { + "string": "30a-1a2a0.3a13a90a8a21a0a8a0a0a45a0a0a0a0a50a1a89a149a0.396078a0a0.27451a0a0.14902a0a1a0a31a1a145a68a0.141176a0a0.0352941a0a0.0313726a0a1a0a0.5a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": [ + -1.0, + 0.0 + ], + "scale": [ + 1.435, + 0.935 + ] + }, + { + "string": "30a-1a1a0.3a13a90a8a21a0a0a0a0a45a0a0a0a0a21a1a89a149a0.894118a0a0.631373a0a0.345098a0a1a0a13a1a145a68a0.894118a0a0.631373a0a0.345098a0a1a0a0.5a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a183a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": null, + "scale": [ + 2.322, + 1.347 + ] + }, + { + "string": "19a-1a2a0.3a8a90a0a29a0a5a0a0a0a0a0a0a0a50a1a50a33a0.12549a0a0.0862745a0a0a0a1a0a30a1a50a30a0.12549a0a0.0862745a0a0a0a1a0a0.1a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a181a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": null, + "scale": [ + 1.145, + 1.795 + ] + }, + { + "string": "31a-1a3a0.3a9a90a27a11a0a37a38a0a0a0a0a0a0a50a1a0a0a0.133333a0a0.0862745a0a0.0588235a0a1a0a50a1a0a0a0.0666667a0a0.0470588a0a0.0352941a0a1a0a0.5a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": [ + 1.0, + -23.0 + ], + "scale": [ + 1.27, + 1.832 + ] + }, + { + "string": "40a-1a3a0.3a12a90a13a11a0a48a78a0a0a0a0a0a0a5a3a0a0a0.439216a0a0.309804a0a0.129412a0a1a0a3a3a0a0a0.439216a0a0.309804a0a0.129412a0a1a0a0.1a0a0.3a0a0a0a0a0a0a0a0a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": [ + 2.0, + -20.0 + ], + "scale": [ + 1.205, + 1.28 + ] + } + ] + }, + "Ice": { + "background": { + "sprite": "ice_gauntlet.png", + "scale": 1.27 + }, + "gauntlet": { + "position": [ + 0.0, + 15.0 + ], + "scale": 1.0 + }, + "particles": [ + { + "string": "25a-1a1a0.3a19a90a90a0a0a0a0a0a0a0a0a0a0a12a1a0a98a0.0980392a0a0.176471a0a0.219608a0a1a0a36a1a0a0a0.0980392a0a0.176471a0a0.219608a0a1a0a0a0a0a0a0a0a0a0a0a0a0a2a1a0a0a0a191a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": [ + 0.0, + 17.0 + ], + "scale": [ + 3.76, + 3.76 + ] + }, + { + "string": "200a-1a4a0.3a46a90a0a18a0a24a0a0a0a0a0a0a0a1a1a0a39a0.980392a0a1a0a0.980392a0a1a0a1a1a98a41a0.980392a0a1a0a0.980392a0a1a0a0.3a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a14a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": [ + 11.0, + 112.0 + ], + "scale": [ + 2.322, + 3.097 + ] + }, + { + "string": "44a-1a3a0.3a13a90a27a11a0a40a62a0a0a0a0a0a0a50a1a0a0a0.0431373a0a0.0627451a0a0.0666667a0a1a0a50a1a0a0a0.0431373a0a0.0627451a0a0.0666667a0a1a0a0.5a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": [ + 0.0, + -11.0 + ], + "scale": [ + 1.145, + 1.795 + ] + }, + { + "string": "38a-1a1.5a0.3a21a90a90a0a0a31a69a0a0a0a0a0a0a2a1a65a57a1a0a1a0a1a0a1a0a5a1a0a0a1a0a1a0a1a0a1a0a0.2a0a0.4a0a0a0a0a0a0a0a0a2a1a0a0a0a169a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": null, + "scale": [ + 1.47, + 1.957 + ] + } + ] + }, + "Poison": { + "background": { + "sprite": "poison_gauntlet.png", + "scale": 1.27 + }, + "particles": [ + { + "string": "31a-1a3a0.3a9a90a27a11a0a37a64a0a0a0a0a0a0a50a1a0a0a0.054902a0a0.129412a0a0.0627451a0a1a0a50a1a0a0a0.054902a0a0.129412a0a0.0627451a0a1a0a0.5a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": [ + 0.0, + 11.0 + ], + "scale": [ + 1.26, + 1.485 + ] + }, + { + "string": "20a-1a3a0.3a6a90a22a11a0a33a55a0a0a0a0a0a0a1a1a0a0a0.137255a0a0.301961a0a0.133333a0a1a0a10a1a0a0a0.137255a0a0.301961a0a0.133333a0a1a0a0.1a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a40a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": [ + -3.0, + -13.0 + ], + "scale": [ + 1.722, + 1.397 + ] + }, + { + "string": "4a-1a2.5a0.3a1a-90a0a0a10a35a0a0a-70a0a0a0a0a6a2a140a0a0.345098a0a0.690196a0a0.301961a0a1a0a4a2a140a0a0.345098a0a0.690196a0a0.301961a0a1a0a0a0a0a0a0a0a0a0a0a0a0a2a1a0a0a0a77a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": [ + 0.0, + 109.0 + ], + "scale": [ + 1.145, + 1.795 + ] + }, + { + "string": "40a-1a3a0.3a12a90a13a11a0a48a78a0a0a0a0a0a0a3a1a0a0a0.129412a0a0.439216a0a0.168627a0a1a0a1a1a0a0a0.129412a0a0.439216a0a0.168627a0a1a0a0.1a0a0.3a0a0a0a0a0a0a0a0a2a1a0a0a0a192a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": null, + "scale": [ + 1.27, + 1.632 + ] + } + ] + }, + "Shadow": { + "background": { + "sprite": "shadow_gauntlet.png", + "scale": 1.745 + }, + "gauntlet": { + "position": [ + 2.0, + 26.0 + ], + "scale": 1.0 + }, + "particles": [ + { + "string": "48a-1a3a0.3a14a90a27a6a0a40a57a0a0a0a0a0a0a50a1a0a0a0.0509804a0a0.0235294a0a0.0666667a0a1a0a50a1a0a0a0.0509804a0a0.0235294a0a0.0666667a0a1a0a0.5a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": [ + 0.0, + -15.0 + ], + "scale": [ + 1.31, + 1.735 + ] + }, + { + "string": "62a-1a1a0.3a47a90a90a29a0a11a0a0a0a0a0a0a0a50a1a0a59a0.0784314a0a0.0235294a0a0.0941176a0a1a0a38a1a103a65a0.0784314a0a0.0235294a0a0.0941176a0a1a0a0.5a0a0.5a0a0a0a0a0a0a0a1a2a1a0a0a0a190a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": [ + 0.0, + 25.0 + ], + "scale": [ + 2.472, + 2.472 + ] + }, + { + "string": "62a-1a1a0.3a47a90a180a29a0a11a0a0a0a0a0a0a0a50a1a0a0a0.0313726a0a0.00392157a0a0.0470588a0a1a0a38a1a0a0a0.0313726a0a0.00392157a0a0.0470588a0a1a0a0.5a0a0.5a0a38a0a9a0a-85a26a1a2a1a0a0a0a183a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": [ + 0.0, + 25.0 + ], + "scale": [ + 1.47, + 1.47 + ] + }, + { + "string": "62a-1a1a0.3a47a90a180a29a0a11a0a0a0a0a0a0a0a50a1a0a0a0.0313726a0a0.00392157a0a0.0431373a0a1a0a38a1a0a0a0.0313726a0a0.00392157a0a0.0431373a0a1a0a0.5a0a0.5a0a38a0a9a0a-85a26a1a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": [ + 0.0, + 25.0 + ], + "scale": [ + 1.195, + 1.195 + ] + }, + { + "string": "40a-1a3a0.3a12a90a13a11a0a48a78a0a0a0a0a0a0a5a3a0a0a0.129412a0a0.439216a0a0.133333a0a1a0a3a3a0a0a0.129412a0a0.439216a0a0.133333a0a1a0a0.1a0a0.3a0a0a0a0a0a0a0a0a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": [ + 0.0, + -16.0 + ], + "scale": [ + 1.255, + 1.43 + ] + } + ] + }, + "Lava": { + "background": { + "sprite": "lava_gauntlet.png", + "scale": 1.27 + }, + "gauntlet": { + "position": [ + 0.0, + 16.0 + ], + "scale": 1.0 + }, + "particles": [ + { + "string": "40a-1a3a0.3a12a90a13a11a0a48a78a0a0a0a0a0a0a5a3a0a0a0.439216a0a0.309804a0a0.129412a0a1a0a3a3a0a0a0.439216a0a0.309804a0a0.129412a0a1a0a0.1a0a0.3a0a0a0a0a0a0a0a0a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": [ + 0.0, + -16.0 + ], + "scale": [ + 1.36, + 1.36 + ] + }, + { + "string": "59a-1a1a0.3a45a90a14a100a54a2a6a0a-135a0a46a0a0a20a8a0a0a1a0a0.756863a0a0.258824a0a1a0a1a1a0a0a1a0a0.694118a0a0.321569a0a1a0a0a0a0.1a0a0a0a0a0a0a0a0a2a1a0a0a1a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": null, + "scale": [ + 1.522, + 1.547 + ] + }, + { + "string": "56a-1a1a0.3a43a90a16a103a81a2a6a0a-180a0a46a0a0a13a8a0a0a1a0a0.756863a0a0.258824a0a1a0a1a1a0a0a1a0a0.694118a0a0.321569a0a1a0a0a0a0.1a0a0a0a0a0a0a0a0a2a1a0a0a1a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": null, + "scale": [ + 1.47, + 1.37 + ] + }, + { + "string": "56a-1a1a0.5a19a90a28a49a43a2a6a0a23a0a43a0a0a50a33a0a0a0.172549a0a0.160784a0a0.14902a0a0.47a0a50a38a0a0a0.247059a0a0.243137a0a0.239216a0a0.47a0a0a0a0.13a0a0a0a0a0a0a0a0a2a0a0a0a1a29a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": null, + "scale": [ + 1.095, + 0.845 + ] + }, + { + "string": "59a-1a1a0.3a45a90a18a113a30a2a6a0a-192a0a46a0a0a20a8a0a0a1a0a0.505882a0a0.258824a0a1a0a1a1a0a0a1a0a0.505882a0a0.258824a0a1a0a0a0a0.1a0a0a0a0a0a0a0a0a2a1a0a0a1a158a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": null, + "scale": [ + 1.255, + 1.43 + ] + }, + { + "string": "48a-1a3a0.3a14a90a27a6a0a40a57a0a0a0a0a0a0a50a1a0a0a0.0666667a0a0.0352941a0a0.0196078a0a1a0a50a1a0a0a0.0666667a0a0.0352941a0a0.0196078a0a1a0a0.5a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": [ + 0.0, + -18.0 + ], + "scale": [ + 1.235, + 1.835 + ] + } + ] + }, + "Time": { + "background": { + "sprite": "time_gauntlet.png", + "scale": 1.22 + } + }, + "Spike": { + "background": { + "sprite": "spike_gauntlet.png", + "scale": 1.92 + } + }, + "Cyborg": { + "background": { + "sprite": "cyborg_gauntlet.png", + "scale": 1.27 + } + }, + "Inferno": { + "background": { + "sprite": "inferno_gauntlet.png", + "scale": 1.27 + } + }, + "Death": { + "background": { + "sprite": "death_gauntlet.png", + "scale": 1.27 + } + }, + "Magic": { + "background": { + "sprite": "magic_gauntlet.png", + "scale": 1.27 + } + }, + "Chaos": { + "background": { + "sprite": "chaos_gauntlet.png", + "scale": 1.27 + } + }, + "Force": { + "background": { + "sprite": "force_gauntlet.png", + "scale": 1.27 + } + }, + "NCS I": { + "background": { + "sprite": "ncs_gauntlet_1.png", + "scale": 1.27 + }, + "gauntlet": { + "position": [ + 0.0, + 16.0 + ], + "scale": 1.0 + }, + "particles": [ + { + "string": "25a-1a1a0.3a19a90a90a0a0a0a0a0a0a0a0a0a0a23a1a0a98a0.0980392a0a0.176471a0a0.219608a0a1a0a50a1a0a0a0.0980392a0a0.176471a0a0.219608a0a1a0a0a0a0a0a0a0a0a0a0a0a0a2a1a0a0a0a185a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": [ + 0.0, + 20.0 + ], + "scale": [ + 1.0, + 1.0 + ] + }, + { + "string": "25a-1a1a0.3a19a90a90a0a0a0a0a0a0a0a0a0a0a12a1a0a98a0.0980392a0a0.176471a0a0.219608a0a1a0a36a1a0a0a0.0980392a0a0.176471a0a0.219608a0a1a0a0a0a0a0a0a0a0a0a0a0a0a2a1a0a0a0a167a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": null, + "scale": [ + 1.0, + 1.0 + ] + }, + { + "string": "30a-1a2.5a0.3a10a90a12a19a0a23a0a0a11a0a0a0a0a4a1a0a0a0.313726a0a0.458824a0a0.529412a0a1a0a2a1a0a0a0.313726a0a0.458824a0a0.529412a0a1a0a0a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a19a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "position": [ + 0.0, + -81.0 + ], + "scale": [ + 1.0, + 1.0 + ] + } + ] + } +} diff --git a/src/hooks/CustomBadges.cpp b/src/hooks/CustomBadges.cpp new file mode 100644 index 0000000..2e50da5 --- /dev/null +++ b/src/hooks/CustomBadges.cpp @@ -0,0 +1,467 @@ +#include "Geode/utils/async.hpp" +#include "Geode/utils/string.hpp" +#include +#include +#include +#include + +std::string s_cachedBadgesResponse = ""; + +static void requestBadges(async::TaskHolder listener, std::function callback) { + if (!s_cachedBadgesResponse.empty()) { + callback(s_cachedBadgesResponse); + } else { + listener.spawn(web::WebRequest().get("https://clarifygdps.com/gdutils/gdutils_roles.php?version=1.0"), [callback](web::WebResponse value) { + if (value.ok()) { + auto res = value.string().unwrapOrDefault(); + callback(std::move(res)); + } else { + log::error("Failed to get GDUtils badges: {}", value.error()); + } + }); + } +} + +static void onRobTopBadgePressed() { + FLAlertLayer::create( + nullptr, + "RobTop Games", + R"text( +RobTop is the creator and the main developer of Geometry Dash. + +He is the only developer of the game and is responsible for all the updates and content that is added to the game. + )text", + "OK", + nullptr, + 300.0f + )->show(); +} +static void onModBadgePressed(int badgeID) { + switch (badgeID) { + case 1: + #ifndef GEODE_IS_ANDROID + FLAlertLayer::create( + nullptr, + "Geometry Dash Moderator", + R"text( +A Geometry Dash Moderator is a user that has been chosen by RobTop and other Moderators on the team by suggestions. + +Their first task is to suggest levels to be rated to RobTop, the current developer of the game and the maintainer of the level rating system, he is the only one that can rate levels. + +How to become a Geometry Dash Moderator? + +Many community members want to become a Moderator to be able to suggest levels for a rating. Similar to rating, Moderator promotions are solely done by RobTop himself on a case-by-case basis, so there are no clear steps to getting it. Here are some general criteria: +- Be nice. +- Be active in the community. +- Be trustworthy. +- Be able to judge levels well. + +If you display these qualities over time, there is a chance that other Moderators may suggest you to RobTop. Of course, showing that you are qualified may be difficult if you do not share circles with pre-existing Moderators. If you are interested, you can apply to join the team using our application form. Make sure you read the information in it clearly before submitting. Here is the link: + +https://forms.gle/XyRGtFzD3hshDUQV7 + )text", + "OK", + nullptr, + 400.0f, + true, + 100.0f, + 1.0f + )->show(); + #else + FLAlertLayer::create( + nullptr, + "Geometry Dash Moderator", + R"text( +A Geometry Dash Moderator is a user that has been chosen by RobTop and other Moderators on the team by suggestions. + +Their first task is to suggest levels to be rated to RobTop, the current developer of the game and the maintainer of the level rating system, he is the only one that can rate levels. + )text", + "OK", + nullptr, + 400.0f, + true, + 100.0f, + 1.0f + )->show(); + + #endif + break; + case 2: + #ifndef GEODE_IS_ANDROID // android height broken + FLAlertLayer::create( + nullptr, + "Geometry Dash Elder Moderator", + R"text( +A Geometry Dash Elder Moderator is a user that has been chosen by RobTop and other Moderators on the team by suggestions and was already a Geometry Dash Moderator before his promotion. They are the pillars of the moderation team. + +Their first task is to suggest levels to be rated to RobTop, the current developer of the game and the maintainer of the level rating system, which he is the only one that can rate levels. But they also have the power to moderate comments, whitelists Newgrounds artists and unlocks updates for old rated levels. + +How to become a Geometry Dash Moderator? + +Many community members want to become a Moderator to be able to suggest levels for a rating. Similar to rating, Moderator promotions are solely done by RobTop himself on a case-by-case basis, so there are no clear steps to getting it. Here are some general criteria: +- Be nice. +- Be active in the community. +- Be trustworthy. +- Be able to judge levels well. + +If you display these qualities over time, there is a chance that other Moderators may suggest you to RobTop. Of course, showing that you are qualified may be difficult if you do not share circles with pre-existing Moderators. If you are interested, you can apply to join the team using our application form. Make sure you read the information in it clearly before submitting. Here is the link: + +https://forms.gle/XyRGtFzD3hshDUQV7 + )text", + "OK", + nullptr, + 400.0f, + true, + 100.0f, + 1.0f + )->show(); + #else + FLAlertLayer::create( + nullptr, + "Geometry Dash Elder Moderator", + R"text( +A Geometry Dash Elder Moderator is a user that has been chosen by RobTop and other Moderators on the team by suggestions and was already a Geometry Dash Moderator before his promotion. They are the pillars of the moderation team. + +Their first task is to suggest levels to be rated to RobTop, the current developer of the game and the maintainer of the level rating system, which he is the only one that can rate levels. But they also have the power to moderate comments, whitelists Newgrounds artists and unlocks updates for old rated levels. + )text", + "OK", + nullptr, + 400.0f, + true, + 100.0f, + 1.0f + )->show(); + #endif + break; + case 3: + FLAlertLayer::create( + nullptr, + "Geometry Dash Leaderboard Moderator", + R"text( +A Geometry Dash Leaderboard Moderator is a user that has been chosen by RobTop and other Leaderboard Moderators on the team by suggestions. + +Their first task is to moderate the Top 100 / Global leaderboard (as well as level-specific ones) and ban cheaters to keep the leaderboards clean. + +How to become a Geometry Dash Leaderboard Moderator? + +If you are interested, you can apply to join the team using our application form. Make sure you read the information in it clearly before submitting. Here is the link: + +https://docs.google.com/forms/d/e/1FAIpQLSfrJyIVM7KzPyTgzGZ0CSuGBPv40160W9I6bPiMgLRawjJJBg/viewform + )text", + "OK", + nullptr, + 500.0f, + true, + 40.0f, + 1.0f + )->show(); + break; + } +} + +// Mod badges descriptions & GDUtils dev badge +class $modify(GDUtilsProfilePage, ProfilePage) { + struct Fields { + async::TaskHolder m_listener; + }; + + void handleResponse(std::string result, int accountID) { + auto menu = m_mainLayer->getChildByID("username-menu"); + if (!menu) return; + if (result.empty()) return; + s_cachedBadgesResponse = result; + std::vector users = geode::utils::string::split(result, "|"); + for (auto user : users) { + std::vector data = geode::utils::string::split(user, "?"); + int badge = 0; + int accountID_data = 0; + std::string badge_id = ""; + std::string badge_sprite = ""; + float badge_scale = 1.0f; + std::string alertTitle = ""; + std::string alertDesc = ""; + float alertWidth = 300.0f; + bool isKofi = false; + while (data.size() > 0) { + std::string id = data[0]; + std::string name = data[1]; + if (id == "1") { + accountID_data = numFromString(name).unwrapOrDefault(); + } else if (id == "3") { + badge = numFromString(name).unwrapOrDefault(); + } else if (id == "4") { + badge_sprite = name; + } else if (id == "5") { + badge_id = name; + } else if (id == "6") { + badge_scale = numFromString(name).unwrapOrDefault(); + } else if (id == "8") { + alertTitle = name; + } else if (id == "9") { + alertDesc = name; + } else if (id == "10") { + alertWidth = numFromString(name).unwrapOrDefault(); + } else if (id == "11") { + isKofi = name == "1"; + } + data.erase(data.begin()); + data.erase(data.begin()); + } + if (accountID_data == accountID) { + if (!m_mainLayer->getChildByID(badge_id)) { + Build::createSpriteName(fmt::format("{}"_spr, badge_sprite).c_str()).scale(badge_scale).intoMenuItem([=]() { + if (isKofi) { + geode::createQuickPopup( + alertTitle.c_str(), + geode::utils::string::replace(alertDesc, "\\n", "\n").c_str(), + "OK", "Ko-Fi", + [](auto, bool btn2) { + if (btn2) { + geode::utils::web::openLinkInBrowser("https://ko-fi.com/gdutils"); + } + }, + alertWidth + ); + } else { + FLAlertLayer::create( + nullptr, + alertTitle.c_str(), + alertDesc.c_str(), + "OK", + nullptr, + alertWidth + )->show(); + } + }).id(badge_id).parent(menu); + menu->updateLayout(); + } + } + } + } + + void loadPageFromUserInfo(GJUserScore* score) { + ProfilePage::loadPageFromUserInfo(score); + if (m_mainLayer) { + auto menu = m_mainLayer->getChildByID("username-menu"); + if (!menu) return; + if (auto modBadge = menu->getChildByID("mod-badge")) { + modBadge->removeFromParentAndCleanup(true); + if (score->m_userName == "RobTop" && Mod::get()->getSettingValue("customBadges")) { + Build::createSpriteName("robtop_badge.png"_spr).intoMenuItem([]() { + onRobTopBadgePressed(); + }).id("mod-badge").parent(menu); + } else { + int badgeID = score->m_modBadge; + Build::createSpriteName(fmt::format("modBadge_0{}_001.png", badgeID).c_str()).intoMenuItem([badgeID]() { + onModBadgePressed(badgeID); + }).id("mod-badge").parent(menu); + } + } + if (Mod::get()->getSettingValue("customBadges")) { + if (!s_cachedBadgesResponse.empty()) { + handleResponse(s_cachedBadgesResponse, score->m_accountID); + } else { + m_fields->m_listener.spawn(web::WebRequest().get("https://clarifygdps.com/gdutils/gdutils_roles.php?version=1.0"), [this, accountID = std::move(score->m_accountID)](web::WebResponse value) { + if (value.ok()) { + auto res = value.string().unwrapOrDefault(); + handleResponse(std::move(res), accountID); + } else { + log::error("Failed to get GDUtils badges: {}", value.error()); + } + }); + } + } + menu->updateLayout(); + } + } +}; + +// Comments +#include + +std::string badgesDataComments = ""; + +int hexStringToInt(const std::string& hexStr) { + int value; + std::istringstream iss(hexStr); + iss >> std::hex >> value; + return value; +} + +class $modify(CommentCell) { + struct Fields { + async::TaskHolder m_listener; + }; + + void handleResponse(std::string result, int commentID) { + auto menu = m_mainLayer->getChildByIDRecursive("username-menu"); + if (!menu) return; + if (this->m_accountComment) return; + if (result.empty()) return; + s_cachedBadgesResponse = result; + std::vector users = geode::utils::string::split(result, "|"); + + CommentCell* cell = this; + int accountID = cell->m_comment->m_accountID; + bool colorSet = false; + + for (auto user : users) { + std::vector data = geode::utils::string::split(user, "?"); + int badge = 0; + int accountID_data = 0; + std::string badge_id = ""; + std::string badge_sprite = ""; + float badge_scale = 1.0f; + std::string alertTitle = ""; + std::string alertDesc = ""; + float alertWidth = 300.0f; + bool isKofi = false; + ccColor3B color = { 255, 255, 255 }; + + while (data.size() > 0) { + std::string id = data[0]; + std::string name = data[1]; + if (id == "1") { + accountID_data = geode::utils::numFromString(name).unwrapOrDefault(); + } else if (id == "3") { + badge = geode::utils::numFromString(name).unwrapOrDefault(); + } else if (id == "4") { + badge_sprite = name; + } else if (id == "5") { + badge_id = name; + } else if (id == "7") { + badge_scale = geode::utils::numFromString(name).unwrapOrDefault(); + } else if (id == "8") { + alertTitle = name; + } else if (id == "9") { + alertDesc = name; + } else if (id == "10") { + alertWidth = geode::utils::numFromString(name).unwrapOrDefault(); + } else if (id == "11") { + isKofi = name == "1"; + } else if (id == "12") { + // Get color from HEX + color = ccc3( + hexStringToInt(name.substr(0, 2)), + hexStringToInt(name.substr(2, 2)), + hexStringToInt(name.substr(4, 2)) + ); + } + data.erase(data.begin()); + data.erase(data.begin()); + } + + if (accountID_data == accountID) { + if (!m_mainLayer->getChildByIDRecursive(badge_id)) { + auto btn = Build::createSpriteName(fmt::format("{}"_spr, badge_sprite).c_str()).scale(badge_scale).intoMenuItem([=]() { + if (isKofi) { + geode::createQuickPopup( + alertTitle.c_str(), + geode::utils::string::replace(alertDesc, "\\n", "\n").c_str(), + "OK", "Ko-Fi", + [](auto, bool btn2) { + if (btn2) { + geode::utils::web::openLinkInBrowser("https://ko-fi.com/gdutils"); + } + }, + alertWidth + ); + } else { + FLAlertLayer::create( + nullptr, + alertTitle.c_str(), + alertDesc.c_str(), + "OK", + nullptr, + alertWidth + )->show(); + } + }).id(badge_id).collect(); + + if (auto textArea = cell->getChildByType(0)) { + CCArrayExt children = textArea->m_label->getChildren(); + for (auto label : children) { + if (color.r != 255 && color.g != 255 && color.b != 255 && !colorSet) { + label->setColor(color); + colorSet = true; + } + } + } + if (auto commentText = static_cast(cell->getChildByID("comment-text-label"))) { + if (color.r != 255 && color.g != 255 && color.b != 255 && !colorSet) { + commentText->setColor(color); + cell->m_comment->m_color = color; + colorSet = true; + } + } + if (cell->getChildByIDRecursive("percentage-label")) { + menu->insertBefore(btn, cell->getChildByIDRecursive("percentage-label")); + } else { + menu->addChild(btn); + } + menu->updateLayout(); + } + } + } + } + + void loadFromComment(GJComment* comment) { + CommentCell::loadFromComment(comment); + if (comment->m_commentDeleted) return; + auto menu = m_mainLayer->getChildByIDRecursive("username-menu"); + if (!menu) return; + if (auto modBadge = menu->getChildByID("mod-badge")) { + modBadge->removeFromParentAndCleanup(true); + CCMenuItemSpriteExtra* badgeBtn; + if (m_comment->m_userName == "RobTop" && Mod::get()->getSettingValue("customBadges")) { + Build::createSpriteName("robtop_badge.png"_spr).scale(0.75f).intoMenuItem([]() { + onRobTopBadgePressed(); + }).id("mod-badge").store(badgeBtn); + } else { + int badgeID = comment->m_modBadge; + Build::createSpriteName(fmt::format("modBadge_0{}_001.png", badgeID).c_str()).scale(0.6f).intoMenuItem([badgeID]() { + onModBadgePressed(badgeID); + }).id("mod-badge").store(badgeBtn); + } + if (getChildByIDRecursive("percentage-label")) { + menu->insertBefore(badgeBtn, getChildByIDRecursive("percentage-label")); + } else { + menu->addChild(badgeBtn); + } + } + if (Mod::get()->getSettingValue("customBadges")) { + if (!s_cachedBadgesResponse.empty()) { + handleResponse(s_cachedBadgesResponse, m_comment->m_commentID); + } else { + m_fields->m_listener.spawn(web::WebRequest().get("https://clarifygdps.com/gdutils/gdutils_roles.php?version=1.0"), [this](web::WebResponse value) { + if (value.ok()) { + auto res = value.string().unwrapOrDefault(); + handleResponse(std::move(res), m_comment->m_commentID); + } else { + log::error("Failed to get GDUtils badges: {}", value.error()); + } + }); + } + } + menu->updateLayout(); + } +}; + +static void toggleHook(bool value) { + for (auto& hook : geode::Mod::get()->getHooks()) { + if (hook->getDisplayName().starts_with("ProfilePage") || hook->getDisplayName().starts_with("CommentCell")) { + (void)(value ? hook->enable() : hook->disable()); + } + } +} +$execute { + listenForSettingChanges("clickableBadges", [](bool value) { + log::info("Clickable Badges changed to {}", value); + toggleHook(value); + }); +} +$on_mod(Loaded) { + if (!Mod::get()->getSettingValue("clickableBadges")) { + toggleHook(false); + } +} diff --git a/src/hooks/DailyLevelPage.cpp b/src/hooks/DailyLevelPage.cpp new file mode 100644 index 0000000..4c83823 --- /dev/null +++ b/src/hooks/DailyLevelPage.cpp @@ -0,0 +1,40 @@ +#include +#include +#include + +class $modify(EventPage, DailyLevelPage) { + bool init(GJTimedLevelType type) { + if (!DailyLevelPage::init(type)) return false; + + if (type != GJTimedLevelType::Event) return true; + + if (auto menu = this->m_mainLayer->getChildByIDRecursive("main-menu")) { + Build::createSpriteName("info_btn.png"_spr).intoMenuItem([]() { + EventInfoPopup::create()->show(); + }).pos(14,-194).parent(menu); + } + return true; + } +}; + +static void toggleHook(bool value) { + for (auto& hook : geode::Mod::get()->getHooks()) { + if (hook->getDisplayName().starts_with("DailyLevelPage")) { + (void)(value ? hook->enable() : hook->disable()); + } + } +} + +$execute { + listenForSettingChanges("eventLevelInfo", [](bool value) { + log::info("Event Level Info changed to {}", value); + toggleHook(value); + }); +} + +$on_mod(Loaded) { + if (!Mod::get()->getSettingValue("eventLevelInfo")) { + toggleHook(false); + } +} + diff --git a/src/hooks/FriendsProfilePage.cpp b/src/hooks/FriendsProfilePage.cpp new file mode 100644 index 0000000..7f30924 --- /dev/null +++ b/src/hooks/FriendsProfilePage.cpp @@ -0,0 +1,153 @@ +#include +#include +#include + +class $modify(FriendPage, FriendsProfilePage) { + bool init(UserListType type) { + if (!FriendsProfilePage::init(type)) return false; + Build::createSpriteName("gj_findBtn_001.png").intoMenuItem([this]() { + if (!m_users) { + youhavenofriends: + FLAlertLayer::create(nullptr, + "Error", + "You have no friends!", + "OK", + nullptr, + 200.0F + )->show(); + return; + } + if (!m_listLayer->m_list) + return; + auto customList = m_listLayer->m_list->m_tableView; + if (customList == nullptr) return; + if (customList->m_contentLayer && customList->m_contentLayer->getChildrenCount() <= 0) { + goto youhavenofriends; + } + SearchUserLayer::create(this)->show(); + }).pos(15, -135).parent(m_buttonMenu); + return true; + } + void setupUsersBrowser(cocos2d::CCArray * users, UserListType type) { + FriendsProfilePage::setupUsersBrowser(users, type); + m_buttonMenu->removeChildByID("friendsScrollBar"_spr); + } + virtual void getUserListFinished(cocos2d::CCArray* a1, UserListType a2) { + FriendsProfilePage::getUserListFinished(a1,a2); + // Add custom scrollbar + auto scrollBar = Scrollbar::create(m_listLayer->m_list->m_tableView); + scrollBar->setPosition(390, -140); + scrollBar->setID("friendsScrollBar"_spr); + m_buttonMenu->addChild(scrollBar); + } + virtual void onUpdate(UserListType a1) { + FriendsProfilePage::forceReloadList(a1); + m_buttonMenu->removeChildByID("friendsScrollBar"_spr); + } +}; + +void SearchUserLayer::onValidate() { + if (auto obj = m_friendPage.lock()) { + std::string username = input_username->getString(); + // If we couldn't find the comment list layer, return + if (obj->m_listLayer == nullptr) { + // safeguard from crashing + FLAlertLayer::create(nullptr, + "Error", + "The mod could not find the GJCommentListLayer layer. Please either try again later, removing mods that may be interfering with the scene, or report this to the developers.", + "OK", + nullptr, + 350.0F + )->show(); + return; + } + if (!obj->m_users) { +youhavenofriends: + FLAlertLayer::create(nullptr, + "Error", + "You have no friends!", + "OK", + nullptr, + 200.0F + )->show(); + return; + } + if (!obj->m_listLayer->m_list) + return; + auto customList = obj->m_listLayer->m_list->m_tableView; + + // If the list is null, return + if (customList == nullptr) return; + + if (customList->m_contentLayer && customList->m_contentLayer->getChildrenCount() <= 0) { + goto youhavenofriends; + } + + auto contentLayer = customList->m_contentLayer; + + int counter_page = 0; + bool found = false; + + auto cells = CCArrayExt(customList->m_contentLayer->getChildren()); + + // Iterate through all cells in the list + for (auto cell : cells) { + std::string inputUsername = geode::utils::string::toLower(cell->m_userScore->m_userName); + + // If username is found in the cell's username (case insensitive) + if (inputUsername.find(geode::utils::string::toLower(username)) != std::string::npos) { + customList->scrollLayer(-9999999); + customList->scrollLayer(counter_page); + found = true; + // Pulse only one time cell color animation + if (auto layer = cell->getChildByType(0)) { + auto originalColor = layer->getColor(); + auto tintTo1 = CCTintTo::create(0.2f, 0, 255, 0); // Change to green + auto tintTo2 = CCTintTo::create(0.5f, originalColor.r, originalColor.g, originalColor.b); // Change back to original color + auto sequence = CCSequence::create(tintTo1, tintTo2, nullptr); + cell->getChildByType(0)->runAction(sequence); + } + break; + } + counter_page += 45; + } + + // If user not found, show alert + if (!found) { + std::string str = username; + FLAlertLayer::create(nullptr, + "Error", + "User not found : " + str, + "OK", + nullptr, + 200.0F + )->show(); + } + } + Popup::onClose(nullptr); +} + +static void toggleHook(bool value) { + for (auto& hook : geode::Mod::get()->getHooks()) { + if (hook->getDisplayName().starts_with("FriendsProfilePage")) { + if (value) { + (void)hook->enable(); + } else { + (void)hook->disable(); + } + } + } +} + +$execute { + listenForSettingChanges("friendSearch", [](bool value) { + log::info("Friend Search changed to {}", value); + toggleHook(value); + }); +} + +$on_mod(Loaded) { + if (!Mod::get()->getSettingValue("friendSearch")) { + toggleHook(false); + } +} diff --git a/src/hooks/GJGarageLayer.cpp b/src/hooks/GJGarageLayer.cpp new file mode 100644 index 0000000..018508d --- /dev/null +++ b/src/hooks/GJGarageLayer.cpp @@ -0,0 +1,74 @@ +#include +#include +class $modify(ItemInfoPopup) { + bool init(int id, UnlockType type) { + if (!ItemInfoPopup::init(id, type)) return false; + if (auto scene = CCScene::get()) { + if (scene->getChildByID("GJGarageLayer")) { + GameManager* gm = GameManager::sharedState(); + GJItemIcon* node = m_mainLayer->getChildByType(0); + if (node && node->m_isIcon) { + auto player = static_cast(node->m_player); + player->setColor(gm->colorForIdx(gm->getPlayerColor())); + player->setSecondColor(gm->colorForIdx(gm->getPlayerColor2())); + player->m_glowColor = gm->colorForIdx(gm->getPlayerGlowColor()); + + Build::createSpriteName("GJ_checkOff_001.png").scale(.8f).child( + Build::create("P1", "bigFont.fnt").scale(.525f).pos(15, 17) + ).intoMenuItem([player]() { + GameManager* gm = GameManager::sharedState(); + player->setColor(gm->colorForIdx(gm->getPlayerColor())); + player->setSecondColor(gm->colorForIdx(gm->getPlayerColor2())); + }).pos(-128, -40).id("p1-button"_spr).parent(m_buttonMenu); + + Build::createSpriteName("GJ_checkOff_001.png").scale(.8f).child( + Build::create("P2", "bigFont.fnt").scale(.525f).pos(15, 17) + ).intoMenuItem([player]() { + GameManager* gm = GameManager::sharedState(); + player->setColor(gm->colorForIdx(gm->getPlayerColor2())); + player->setSecondColor(gm->colorForIdx(gm->getPlayerColor())); + }).pos(-99, -40).id("p2-button"_spr).parent(m_buttonMenu); + + Build::createSpriteName("GJ_checkOff_001.png").scale(.8f).child( + Build::create("G", "bigFont.fnt").scale(.525f).pos(15, 17) + ).intoMenuItem([player]() { + GameManager* gm = GameManager::sharedState(); + if (!player->m_hasGlowOutline) { + player->setGlowOutline(gm->colorForIdx(gm->getPlayerGlowColor())); + } else { + player->disableGlowOutline(); + } + }).pos(-69, -40).id("glow-button"_spr).parent(m_buttonMenu); + } + + } + } + + return true; + } +}; + +static void toggleHook(bool value) { + for (auto& hook : geode::Mod::get()->getHooks()) { + if (hook->getDisplayName().starts_with("ItemInfoPopup")) { + if (value) { + (void)hook->enable(); + } else { + (void)hook->disable(); + } + } + } +} + +$execute { + listenForSettingChanges("previewIcons", [](bool value) { + log::info("Preview Icons changed to {}", value); + toggleHook(value); + }); +} + +$on_mod(Loaded) { + if (!Mod::get()->getSettingValue("previewIcons")) { + toggleHook(false); + } +} diff --git a/src/hooks/GauntletSelectLayer.cpp b/src/hooks/GauntletSelectLayer.cpp new file mode 100644 index 0000000..365e6d3 --- /dev/null +++ b/src/hooks/GauntletSelectLayer.cpp @@ -0,0 +1,1232 @@ +#include +#include + + +struct GauntletParticles { + std::string particle; + CCPoint pos; + CCPoint scale = CCPoint(1.310f, 2.610f); +}; + +struct NewGauntlet { + std::vector particles; + std::string_view background; + CCPoint bgPos; + float scaleBg; + float scaleGauntlet; +}; + +template<> +struct matjson::Serialize { + static geode::Result fromJson(matjson::Value const& value) { + NewGauntlet data; + GEODE_UNWRAP_INTO(data.background, value["background"]["sprite"].asString()); + GEODE_UNWRAP_INTO(data.scaleBg, value["background"]["scale"].asDouble()); + if (value.contains("gauntlet")) { + GEODE_UNWRAP_INTO(auto bgPos, value["gauntlet"]["position"].asArray()); + data.bgPos = CCPoint(bgPos.at(0).asDouble().unwrapOrDefault(), bgPos.at(1).asDouble().unwrapOrDefault()); + GEODE_UNWRAP_INTO(data.scaleGauntlet, value["gauntlet"]["scale"].asDouble()); + } + if (value.contains("particles")) { + GEODE_UNWRAP_INTO(auto particles, value["particles"].asArray()); + for (auto& particle : particles) { + GauntletParticles particleData; + GEODE_UNWRAP_INTO(particleData.particle, particle["string"].asString()); + auto posOpt = particle["position"].asArray(); + if (posOpt.isOk()) { + auto pos = posOpt.unwrap(); + particleData.pos = CCPoint(pos.at(0).asDouble().unwrapOrDefault(), pos.at(1).asDouble().unwrapOrDefault()); + } + GEODE_UNWRAP_INTO(auto scale, particle["scale"].asArray()); + particleData.scale = CCPoint(scale.at(0).asDouble().unwrapOrDefault(), scale.at(1).asDouble().unwrapOrDefault()); + data.particles.push_back(particleData); + } + } + return geode::Ok(data); + } +}; + +class $modify(CustomGauntletSelectLayer, GauntletSelectLayer) { + void reverseGauntlet(GauntletNode* node) { + if (!node->m_gauntletInfoNode) return; + if (auto checkmark = node->getChildByID("checkmark-sprite")) { + checkmark->setPositionY(78); + CCNode* checkmark_shadow = node->getChildByID("checkmark-shadow-sprite"); + checkmark_shadow->setPositionY(78); + } + if (auto label_reward = node->m_gauntletInfoNode->getChildByID("reward-label")) { + label_reward->setPositionY(50); + CCNode* label_reward_shadow = node->m_gauntletInfoNode->getChildByID("reward-shadow-label"); + label_reward_shadow->setPositionY(50); + + if (CCSprite* chest = static_cast(node->m_gauntletInfoNode->getChildByID("chest-sprite"))) { + chest->setPositionY(80); + chest->setFlipY(true); + } + if (CCSprite* chest_shadow = static_cast(node->m_gauntletInfoNode->getChildByID("chest-shadow-sprite"))) { + chest_shadow->setPositionY(80); + chest_shadow->setFlipY(true); + } + } + if (CCNode* label_progress = node->m_gauntletInfoNode->getChildByID("gauntlet-progress-label")) { + label_progress->setPositionY(29); + } + if (CCNode* label_progress_shadow = node->m_gauntletInfoNode->getChildByID("gauntlet-progress-shadow-label")) { + label_progress_shadow->setPositionY(29); + } + } + + bool init(int p0) { + if (!GauntletSelectLayer::init(p0)) return false; + CCPoint bcrPos; + if (auto node = this->getChildByIDRecursive("bottom-right-corner")) { + bcrPos = node->getPosition(); + } + if (auto menu = this->getChildByIDRecursive("bottom-right-menu")) { + Build::createSpriteName("GJ_infoIcon_001.png").color(236,149,19).intoMenuItem([]() { + FLAlertLayer::create( + nullptr, + "Gauntlets redesign", + R"text( +All new designs for Gauntlets are made by xvia and brift. +Particles are made by GammaReXx. + +Thanks for their contributions! + )text", + "OK", + nullptr, + 300.0f + )->show(); + }).id("particle-info-button"_spr).zOrder(10).with([](CCNode* node) { + CCParticleSystemQuad* particle_info = GameToolbox::particleFromString("26a-1a1.66a0.3a30a90a180a14a0a1a1a0a7a0a0a0a0a2a1a0a0a1a0a0.568627a0a0.12549a0a1a0a1a1a0a0a1a0a0.501961a0a0a0a1a0a0a0a0.46a0a0a0a0a0a0a0a0a2a1a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChildAtPosition(particle_info, Anchor::Center); + particle_info->setZOrder(9); + }).parent(menu); + menu->updateLayout(); + } + return true; + } + + void setupGauntlets() { + GauntletSelectLayer::setupGauntlets(); + if (!m_scrollLayer) return; + auto res = file::readJson(Mod::get()->getResourcesDir() / "particles.json"); + if (res.isErr()) return log::error("Couldn't read particles.json: {}", res.unwrapErr()); + + for (int i = 0; i < m_scrollLayer->m_pages->count(); i++) { + auto page = static_cast(static_cast(m_scrollLayer->m_pages->objectAtIndex(i))->getChildren()->objectAtIndex(0))->getChildren(); + + for (int j = 0; j < page->count(); j++) { + CCNode* item = static_cast(page->objectAtIndex(j)); + + // Handle spaces + if (j == 0 && page->count() > 1) { + item->setPositionX(item->getPositionX() - 25.0f); + } else if (j == 1 && page->count() == 2) { + item->setPositionX(item->getPositionX() + 25.0f); + } else if (j == 2) { + item->setPositionX(item->getPositionX() + 25.0f); + } + + GauntletNode* node = static_cast(item->getChildByIDRecursive("gauntlet-node")); + + CCLabelBMFont* label = static_cast(node->getChildByID("gauntlet-name-label")); + CCNode* label_shadow = node->getChildByID("gauntlet-name-shadow-label"); + CCNode* label_gauntlet = node->getChildByID("gauntlet-label"); + CCNode* label_shadow_gauntlet = node->getChildByID("gauntlet-shadow-label"); + CCNode* bg = node->getChildByID("background"); + + CCNode* gauntlet_node = node->m_gauntletInfoNode; + CCSprite* gauntlet = static_cast(gauntlet_node->getChildByID("gauntlet-sprite")); + CCNode* gauntlet_shadow = gauntlet_node->getChildByID("gauntlet-shadow-sprite"); + + bg->setVisible(false); + label->setVisible(false); + label_shadow->setVisible(false); + label_gauntlet->setVisible(false); + label_shadow_gauntlet->setVisible(false); + gauntlet->setVisible(false); + gauntlet_shadow->setVisible(false); + + std::string gauntletName = label->getString(); + + auto scaleFactor = 4/CCDirector::get()->getContentScaleFactor(); + + /*auto json = res.unwrap(); + bool foundIt = false; + for (auto it = json.begin(); it != json.end(); it++) { + auto val = *it; + if (!val.getKey() || !val.getKey().has_value()) continue; + if (val.getKey().value() == gauntletName) { + foundIt = true; + auto gauntletDataOpt = matjson::Serialize::fromJson(val); + if (gauntletDataOpt.isErr()) { + log::warn("Couldn't parse the gauntlet named {}: {}", gauntletName, gauntletDataOpt.unwrapErr()); + continue; + } + auto gauntletData = gauntletDataOpt.unwrap(); + Build::create(fmt::format("{}"_spr, gauntletData.background).c_str()) + .pos(bg->getPosition()) + .scale(gauntletData.scaleBg/scaleFactor) + .zOrder(0) + .id("gauntlet-background"_spr) + .parent(node); + + if (gauntletName == "Strange") { + reverseGauntlet(node); + } + + if (gauntletData.scaleGauntlet != 0) { + // Show gauntlets sprite again + gauntlet->setVisible(true); + gauntlet->setPosition(gauntletData.bgPos); + gauntlet->setScale(gauntletData.scaleGauntlet); + gauntlet_node->setZOrder(3); + } + + // Particle + for (size_t i = 0; i < gauntletData.particles.size(); i++) { + auto particleData = gauntletData.particles.at(i); + CCParticleSystemQuad* particle = GameToolbox::particleFromString(particleData.particle, NULL, false); + node->addChild(particle); + particle->setZOrder(1); + if (particleData.pos.x == 0 && particleData.pos.y == 0) { + particle->setPosition(gauntlet->getPosition()); + } else { + particle->setPosition(particleData.pos); + } + particle->setScaleX(particleData.scale.x); + particle->setScaleY(particleData.scale.y); + particle->setID(fmt::format("gauntlet-particles-{}"_spr, i + 1)); + } + } else { + + } + } + if (!foundIt) { + bg->setVisible(true); + label->setVisible(true); + label_shadow->setVisible(true); + label_gauntlet->setVisible(true); + label_shadow_gauntlet->setVisible(true); + gauntlet->setVisible(true); + gauntlet_shadow->setVisible(true); + } + continue;*/ + + if (gauntletName == "World") { + CCSprite* sprite = CCSprite::create("world_gauntlet.png"_spr); + sprite->setPosition(bg->getPosition()); + sprite->setScale(1.270f/scaleFactor); + sprite->setZOrder(0); + sprite->setID("gauntlet-background"_spr); + node->addChild(sprite); + + // Show gauntlets sprite again + gauntlet->setVisible(true); + gauntlet->setPosition({1, 16}); + gauntlet->setScale(1); + gauntlet_node->setZOrder(3); + + // Particle + CCParticleSystemQuad* particle = GameToolbox::particleFromString("22a-1a3a0a20a90a0a29a0a0a0a0a0a0a0a0a0a7a0a0a0a0.729412a0a0.352941a0a0.796078a0a1a0a7a0a0a0a0.729412a0a0.352941a0a0.796078a0a1a0a0.4a0a0.4a0a35a0a35a0a-50a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle); + particle->setPosition({10, 24}); + particle->setZOrder(1); + particle->setScaleX(1.310f); + particle->setScaleY(2.610f); + particle->setID("gauntlet-particles-1"_spr); + CCParticleSystemQuad* particle2 = GameToolbox::particleFromString("22a-1a3a0a20a90a0a29a0a0a0a0a0a0a0a0a0a7a0a0a0a0.34902a0a0.466667a0a0.792157a0a1a0a7a0a0a0a0.34902a0a0.466667a0a0.792157a0a1a0a0.4a0a0.4a0a35a0a35a0a-50a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle2); + particle2->setPosition({6, 24}); + particle2->setZOrder(1); + particle2->setScaleX(1.310f); + particle2->setScaleY(2.610f); + particle2->setID("gauntlet-particles-2"_spr); + CCParticleSystemQuad* particle3 = GameToolbox::particleFromString("22a-1a3a0a20a90a0a29a0a0a0a0a0a0a0a0a0a7a0a0a0a0.360784a0a0.854902a0a0.368627a0a1a0a7a0a0a0a0.360784a0a0.854902a0a0.368627a0a1a0a0.4a0a0.4a0a35a0a35a0a-50a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle3); + particle3->setPosition({2, 24}); + particle3->setZOrder(1); + particle3->setScaleX(1.310f); + particle3->setScaleY(2.610f); + particle3->setID("gauntlet-particles-3"_spr); + CCParticleSystemQuad* particle4 = GameToolbox::particleFromString("22a-1a3a0a20a90a0a29a0a0a0a0a0a0a0a0a0a7a0a0a0a0.866667a0a0.839216a0a0.247059a0a1a0a7a0a0a0a0.866667a0a0.839216a0a0.247059a0a1a0a0.4a0a0.4a0a35a0a35a0a-50a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle4); + particle4->setPosition({-2, 24}); + particle4->setZOrder(1); + particle4->setScaleX(1.310f); + particle4->setScaleY(2.610f); + particle4->setID("gauntlet-particles-4"_spr); + CCParticleSystemQuad* particle5 = GameToolbox::particleFromString("22a-1a3a0a20a90a0a29a0a0a0a0a0a0a0a0a0a7a0a0a0a0.866667a0a0.294118a0a0.247059a0a1a0a7a0a0a0a0.866667a0a0.294118a0a0.247059a0a1a0a0.4a0a0.4a0a35a0a35a0a-50a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle5); + particle5->setPosition({-6, 24}); + particle5->setZOrder(1); + particle5->setScaleX(1.310f); + particle5->setScaleY(2.610f); + particle5->setID("gauntlet-particles-5"_spr); + CCParticleSystemQuad* particle6 = GameToolbox::particleFromString("22a-1a3a0a20a90a0a29a0a0a0a0a0a0a0a0a0a7a0a0a0a0.729412a0a0.352941a0a0.796078a0a1a0a7a0a0a0a0.729412a0a0.352941a0a0.796078a0a1a0a0.4a0a0.4a0a35a0a35a0a-50a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle6); + particle6->setPosition({-9, 24}); + particle6->setZOrder(1); + particle6->setScaleX(-1.310f); + particle6->setScaleY(2.610f); + particle6->setID("gauntlet-particles-6"_spr); + CCParticleSystemQuad* particle7 = GameToolbox::particleFromString("22a-1a3a0a20a90a0a29a0a0a0a0a0a0a0a0a0a7a0a0a0a0.34902a0a0.466667a0a0.792157a0a1a0a7a0a0a0a0.34902a0a0.466667a0a0.792157a0a1a0a0.4a0a0.4a0a35a0a35a0a-50a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle7); + particle7->setPosition({-5, 24}); + particle7->setZOrder(1); + particle7->setScaleX(-1.310f); + particle7->setScaleY(2.610f); + particle7->setID("gauntlet-particles-7"_spr); + CCParticleSystemQuad* particle8 = GameToolbox::particleFromString("22a-1a3a0a20a90a0a29a0a0a0a0a0a0a0a0a0a7a0a0a0a0.360784a0a0.854902a0a0.368627a0a1a0a7a0a0a0a0.360784a0a0.854902a0a0.368627a0a1a0a0.4a0a0.4a0a35a0a35a0a-50a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle8); + particle8->setPosition({-1, 24}); + particle8->setZOrder(1); + particle8->setScaleX(-1.310f); + particle8->setScaleY(2.610f); + particle8->setID("gauntlet-particles-8"_spr); + CCParticleSystemQuad* particle9 = GameToolbox::particleFromString("22a-1a3a0a20a90a0a29a0a0a0a0a0a0a0a0a0a7a0a0a0a0.866667a0a0.839216a0a0.247059a0a1a0a7a0a0a0a0.866667a0a0.839216a0a0.247059a0a1a0a0.4a0a0.4a0a35a0a35a0a-50a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle9); + particle9->setPosition({3, 24}); + particle9->setZOrder(1); + particle9->setScaleX(-1.310f); + particle9->setScaleY(2.610f); + particle9->setID("gauntlet-particles-9"_spr); + CCParticleSystemQuad* particle10 = GameToolbox::particleFromString("22a-1a3a0a20a90a0a29a0a0a0a0a0a0a0a0a0a7a0a0a0a0.866667a0a0.294118a0a0.247059a0a1a0a7a0a0a0a0.866667a0a0.294118a0a0.247059a0a1a0a0.4a0a0.4a0a35a0a35a0a-50a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle10); + particle10->setPosition({7, 24}); + particle10->setZOrder(1); + particle10->setScaleX(-1.310f); + particle10->setScaleY(2.610f); + particle10->setID("gauntlet-particles-10"_spr); + CCParticleSystemQuad* particle11 = GameToolbox::particleFromString("38a-1a1.5a0.3a21a90a90a0a0a31a69a0a0a0a0a0a0a2a1a65a57a1a0a1a0a1a0a1a0a5a1a0a0a1a0a1a0a1a0a1a0a0.2a0a0.4a0a0a0a0a0a0a0a0a2a1a0a0a0a169a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle11); + particle11->setPosition({1, 6}); + particle11->setZOrder(1); + particle11->setScaleX(1.710f); + particle11->setScaleY(1.660f); + particle11->setID("gauntlet-particles-11"_spr); + CCParticleSystemQuad* particle12 = GameToolbox::particleFromString("11a-1a1.5a0.3a6a90a90a0a0a31a69a0a0a0a0a0a0a4a1a0a0a1a0a1a0a1a0a1a0a7a1a52a55a1a0a1a0a1a0a1a0a0.2a0a0.4a0a0a0a0a0a0a0a0a2a1a0a0a0a167a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle12); + particle12->setPosition({1, 16}); + particle12->setZOrder(1); + particle12->setScaleX(1.835f); + particle12->setScaleY(1.485f); + particle12->setID("gauntlet-particles-12"_spr); + } else if (gauntletName == "Fantasy") { + CCSprite* sprite = CCSprite::create("fantasy_gauntlet.png"_spr); + sprite->setPosition(bg->getPosition()); + sprite->setScale(1.225f/scaleFactor); + sprite->setZOrder(0); + sprite->setID("gauntlet-background"_spr); + node->addChild(sprite); + + // Show gauntlets sprite again + gauntlet->setVisible(true); + gauntlet->setPosition({-2, 14}); + gauntlet->setScale(0.925f); + gauntlet_node->setZOrder(2); + + // Particle + CCParticleSystemQuad* particle = GameToolbox::particleFromString("26a-1a5a0.3a4a-90a14a9a0a17a0a0a0a0a0a0a0a2a1a76a325a0.537255a0a0.898039a0a0.164706a0a1a0a2a1a148a149a0.537255a0a0.898039a0a0.164706a0a1a0a0a0a0.3a0a0a0a0a0a0a0a0a2a0a0a0a0a146a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle); + particle->setPosition({gauntlet->getPositionX(), 103}); + particle->setZOrder(1); + particle->setScaleX(2.880f); + particle->setScaleY(4.603f); + particle->setID("gauntlet-particles-1"_spr); + } else if (gauntletName == "Portal") { + CCSprite* sprite = CCSprite::create("portal_gauntlet.png"_spr); + sprite->setPosition(bg->getPosition()); + sprite->setScale(1.225f/scaleFactor); + sprite->setZOrder(0); + sprite->setID("gauntlet-background"_spr); + node->addChild(sprite); + + // Show gauntlets sprite again + gauntlet->setVisible(true); + gauntlet->setPosition({-2, 14}); + gauntlet->setScale(0.925f); + gauntlet_node->setZOrder(2); + + // Particle + CCParticleSystemQuad* particle = GameToolbox::particleFromString("13a-1a1a0.3a10a90a90a29a0a11a0a0a0a0a0a0a0a50a1a0a37a0.376471a0a0.109804a0a0.243137a0a1a0a32a1a142a96a0.376471a0a0.109804a0a0.243137a0a1a0a0.5a0a0.3a0a0a0a0a0a0a0a1a2a1a0a0a0a190a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle); + particle->setPosition({gauntlet->getPositionX(), 14}); + particle->setZOrder(1); + particle->setScaleX(2.880f); + particle->setScaleY(3.978f); + particle->setID("gauntlet-particles-1"_spr); + CCParticleSystemQuad* particle2 = GameToolbox::particleFromString("13a-1a1a0.3a10a90a90a29a0a11a0a0a0a0a0a0a0a50a1a0a37a0.0705882a0a0.172549a0a0.203922a0a1a0a32a1a142a96a0.0705882a0a0.172549a0a0.203922a0a1a0a0.5a0a0.3a0a0a0a0a0a0a0a1a2a1a0a0a0a190a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle2); + particle2->setPosition({gauntlet->getPositionX(), 14}); + particle2->setZOrder(1); + particle2->setScaleX(2.880f); + particle2->setScaleY(3.978f); + particle2->setID("gauntlet-particles-2"_spr); + CCParticleSystemQuad* particle3 = GameToolbox::particleFromString("38a-1a2a0.3a16a90a180a29a0a11a0a0a0a0a0a0a0a6a1a0a0a0.760784a0a0.380392a0a0.835294a0a1a0a3a1a0a0a0.760784a0a0.380392a0a0.835294a0a1a0a0.18a0a0.49a0a43a0a33a0a-34a0a1a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle3); + particle3->setPosition({gauntlet->getPositionX(), 14}); + particle3->setZOrder(1); + particle3->setScaleX(1.030f); + particle3->setScaleY(1.030f); + particle3->setID("gauntlet-particles-3"_spr); + CCParticleSystemQuad* particle4 = GameToolbox::particleFromString("38a-1a2a0.3a16a90a180a29a0a11a0a0a0a0a0a0a0a6a1a0a0a0.376471a0a0.690196a0a0.831373a0a1a0a3a1a0a0a0.376471a0a0.690196a0a0.831373a0a1a0a0.18a0a0.49a0a43a0a33a0a-34a0a1a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle4); + particle4->setPosition({gauntlet->getPositionX(), 14}); + particle4->setZOrder(1); + particle4->setScaleX(1.155f); + particle4->setScaleY(1.355f); + particle4->setID("gauntlet-particles-4"_spr); + CCParticleSystemQuad* particle5 = GameToolbox::particleFromString("30a-1a1a0.3a30a90a180a29a0a11a0a0a0a0a0a0a0a4a1a0a0a0.760784a0a0.380392a0a0.835294a0a1a0a2a1a0a0a0.760784a0a0.380392a0a0.835294a0a1a0a0.18a0a0.49a0a43a0a33a0a-41a0a1a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle5); + particle5->setPosition({gauntlet->getPositionX(), 14}); + particle5->setZOrder(1); + particle5->setScaleX(1.455f); + particle5->setScaleY(1.655f); + particle5->setID("gauntlet-particles-5"_spr); + CCParticleSystemQuad* particle6 = GameToolbox::particleFromString("30a-1a1a0.3a30a90a180a29a0a11a0a0a0a0a0a0a0a4a1a0a0a0.376471a0a0.690196a0a0.831373a0a1a0a2a1a0a0a0.376471a0a0.690196a0a0.831373a0a1a0a0.18a0a0.49a0a43a0a33a0a-41a0a1a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle6); + particle6->setPosition({gauntlet->getPositionX(), 14}); + particle6->setZOrder(1); + particle6->setScaleX(1.355f); + particle6->setScaleY(1.855f); + particle6->setID("gauntlet-particles-6"_spr); + } else if (gauntletName == "Demon") { + CCSprite* sprite = CCSprite::create("demon_gauntlet.png"_spr); + sprite->setPosition(bg->getPosition()); + sprite->setScale(1.275f/scaleFactor); + sprite->setZOrder(0); + sprite->setID("gauntlet-background"_spr); + node->addChild(sprite); + + // Show gauntlets sprite again + gauntlet->setVisible(true); + gauntlet->setPosition({-1, 22}); + gauntlet->setScale(1.1f); + gauntlet_node->setZOrder(2); + + // Particle + CCParticleSystemQuad* particle = GameToolbox::particleFromString("66a-1a3a0.3a20a90a27a11a0a37a38a0a0a0a0a0a0a5a1a0a0a0.694118a0a0.45098a0a0.054902a0a1a0a2a1a0a0a0.509804a0a0.172549a0a0a0a1a0a0.5a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle); + particle->setPosition(gauntlet->getPosition()); + particle->setZOrder(1); + particle->setID("gauntlet-particles-1"_spr); + CCParticleSystemQuad* particle2 = GameToolbox::particleFromString("29a-1a3a0.3a8a90a27a11a0a37a38a0a0a0a0a0a0a50a1a0a0a0.0509804a0a0.0392157a0a0.0352941a0a1a0a50a1a0a0a0.0509804a0a0.0392157a0a0.0352941a0a1a0a0.5a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle2); + particle2->setPosition(gauntlet->getPosition()); + particle2->setZOrder(1); + particle2->setID("gauntlet-particles-2"_spr); + CCParticleSystemQuad* particle3 = GameToolbox::particleFromString("31a-1a1a0.3a23a90a90a29a0a11a0a0a0a0a0a0a0a33a1a89a149a0.454902a0a0.321569a0a0.152941a0a1a0a50a1a145a68a0.454902a0a0.321569a0a0.152941a0a1a0a0.5a0a0.5a0a0a0a0a0a0a0a1a2a1a0a0a0a192a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle3); + particle3->setPosition(gauntlet->getPosition()); + particle3->setZOrder(1); + particle3->setID("gauntlet-particles-3"_spr); + CCParticleSystemQuad* particle4 = GameToolbox::particleFromString("30a-1a2a0.3a13a90a8a21a0a8a0a0a45a0a0a0a0a40a1a89a149a0.396078a0a0.27451a0a0.14902a0a1a0a24a1a145a68a0.141176a0a0.0352941a0a0.0313726a0a1a0a0.5a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle4); + particle4->setPosition(gauntlet->getPosition()); + particle4->setZOrder(1); + particle4->setID("gauntlet-particles-4"_spr); + CCParticleSystemQuad* particle5 = GameToolbox::particleFromString("30a-1a1a0.3a13a90a8a21a0a0a0a0a45a0a0a0a0a21a1a89a149a0.894118a0a0.631373a0a0.345098a0a1a0a6a1a145a68a0.894118a0a0.631373a0a0.345098a0a1a0a0.5a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a183a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle5); + particle5->setPosition(gauntlet->getPosition()); + particle5->setZOrder(1); + particle5->setID("gauntlet-particles-5"_spr); + } else if (gauntletName == "Castle") { + CCSprite* sprite = CCSprite::create("castle_gauntlet.png"_spr); + sprite->setPosition(bg->getPosition()); + sprite->setScale(1.275f/scaleFactor); + sprite->setZOrder(0); + sprite->setID("gauntlet-background"_spr); + node->addChild(sprite); + } else if (gauntletName == "Galaxy") { + CCSprite* sprite = CCSprite::create("galaxy_gauntlet.png"_spr); + sprite->setPosition(bg->getPosition()); + sprite->setScale(1.695f/scaleFactor); + sprite->setZOrder(0); + sprite->setID("gauntlet-background"_spr); + node->addChild(sprite); + + // Show gauntlets sprite again + gauntlet->setVisible(true); + gauntlet->setPosition({0, 11}); + gauntlet->setScale(0.975); + gauntlet_node->setZOrder(3); + + // Particle + CCParticleSystemQuad* particle = GameToolbox::particleFromString("11a-1a1a0.3a8a90a90a0a0a0a0a0a0a0a0a0a0a50a3a0a0a0.627451a0a0.286275a0a0.470588a0a1a0a50a3a106a70a0.627451a0a0.286275a0a0.470588a0a1a0a0a0a0a0a0a0a0a0a0a0a0a2a1a0a0a0a176a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle); + particle->setPosition({0, 24}); + particle->setZOrder(1); + particle->setScaleX(1.610f); + particle->setScaleY(1.610f); + particle->setID("gauntlet-particles-1"_spr); + CCParticleSystemQuad* particle2 = GameToolbox::particleFromString("14a-1a1.2a0.3a9a112a6a42a0a15a0a0a0a0a0a0a0a4a0a340a0a0.913725a0a0.72549a0a0.337255a0a1a0a4a0a340a0a0.913725a0a0.72549a0a0.337255a0a1a0a0.3a0a0.3a0a0a0a0a0a0a0a0a2a1a0a0a0a31a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle2); + particle2->setPosition({-25, 111}); + particle2->setZOrder(1); + particle2->setScaleX(2.660f); + particle2->setScaleY(3.710f); + particle2->setRotation(180); + particle2->setID("gauntlet-particles-2"_spr); + CCParticleSystemQuad* particle3 = GameToolbox::particleFromString("38a-1a1.5a0.3a21a90a90a0a0a31a69a0a0a0a0a0a0a2a1a65a57a0.886275a0a0.866667a0a0.454902a0a1a0a5a1a0a0a0.886275a0a0.866667a0a0.454902a0a1a0a0.2a0a0.4a0a0a0a0a0a0a0a0a2a1a0a0a0a169a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle3); + particle3->setPosition({2, 6}); + particle3->setZOrder(1); + particle3->setScaleX(1.685f); + particle3->setScaleY(1.485f); + particle3->setID("gauntlet-particles-3"_spr); + CCParticleSystemQuad* particle4 = GameToolbox::particleFromString("11a-1a1.5a0.3a6a90a90a0a0a31a69a0a0a0a0a0a0a4a1a0a0a0.890196a0a0.376471a0a0.65098a0a1a0a7a1a52a55a0.890196a0a0.376471a0a0.65098a0a1a0a0.2a0a0.4a0a0a0a0a0a0a0a0a2a1a0a0a0a167a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle4); + particle4->setPosition({2, -2}); + particle4->setZOrder(1); + particle4->setScaleX(1.310f); + particle4->setScaleY(1.635f); + particle4->setID("gauntlet-particles-4"_spr); + CCParticleSystemQuad* particle5 = GameToolbox::particleFromString("4a-1a1a0.3a3a90a90a0a0a0a0a0a0a0a0a0a0a50a3a0a0a0.862745a0a0.352941a0a0.623529a0a1a0a50a3a106a22a0.862745a0a0.352941a0a0.623529a0a1a0a0.3a0a0.3a0a0a0a0a0a0a0a0a2a1a0a0a0a164a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle5); + particle5->setPosition({0, 24}); + particle5->setZOrder(1); + particle5->setScaleX(1.785f); + particle5->setScaleY(1.785f); + particle5->setID("gauntlet-particles-5"_spr); + } else if (gauntletName == "Universe") { + CCSprite* sprite = CCSprite::create("universe_gauntlet.png"_spr); + sprite->setPosition(bg->getPosition()); + sprite->setScale(1.270f/scaleFactor); + sprite->setZOrder(0); + sprite->setID("gauntlet-background"_spr); + node->addChild(sprite); + + // Show gauntlets sprite again + gauntlet->setVisible(true); + gauntlet->setPosition({0, 16}); + gauntlet->setScale(0.975f); + gauntlet_node->setZOrder(3); + + // Particle + CCParticleSystemQuad* particle = GameToolbox::particleFromString("23a-1a3a0.3a6a90a180a29a0a11a0a0a0a0a0a0a0a38a1a0a0a0.0627451a0a0.0980392a0a0.105882a0a1a0a50a1a0a0a0.0627451a0a0.0980392a0a0.105882a0a1a0a0.1a0a0.5a0a10a0a28a0a0a0a1a2a1a0a1a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle); + particle->setPosition({0, 11}); + particle->setZOrder(1); + particle->setScaleX(1.485f); + particle->setScaleY(2.685f); + particle->setID("gauntlet-particles-1"_spr); + CCParticleSystemQuad* particle2 = GameToolbox::particleFromString("20a-1a0.5a0a15a90a0a44a0a11a0a0a0a0a0a0a0a4a1a0a0a0.760784a0a0.447059a0a0.94902a0a1a0a4a1a0a0a0.760784a0a0.447059a0a0.94902a0a1a0a0.1a0a0.1a0a0a0a0a0a0a0a0a2a1a0a0a0a33a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle2); + particle2->setPosition({32, -74}); + particle2->setZOrder(1); + particle2->setScaleX(3.685f); + particle2->setScaleY(8.310f); + particle2->setRotation(-20); + particle2->setID("gauntlet-particles-2"_spr); + CCParticleSystemQuad* particle3 = GameToolbox::particleFromString("19a-1a0.5a0a38a90a0a44a0a20a0a0a0a0a0a0a0a2a1a0a0a0.94902a0a0.67451a0a0.447059a0a1a0a2a1a0a0a0.94902a0a0.67451a0a0.447059a0a1a0a0.1a0a0.1a0a0a0a0a0a0a0a0a2a1a0a0a0a33a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle3); + particle3->setPosition({34, -96}); + particle3->setZOrder(1); + particle3->setScaleX(2.135f); + particle3->setScaleY(8.935f); + particle3->setRotation(-20); + particle3->setID("gauntlet-particles-3"_spr); + CCParticleSystemQuad* particle4 = GameToolbox::particleFromString("20a-1a1a0a15a90a0a36a0a11a0a0a0a0a0a0a0a4a1a0a0a0.447059a0a0.654902a0a0.94902a0a1a0a4a1a0a0a0.447059a0a0.654902a0a0.94902a0a1a0a0.2a0a0.2a0a0a0a0a0a0a0a0a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle4); + particle4->setPosition({41, -92}); + particle4->setZOrder(1); + particle4->setScaleX(3.235f); + particle4->setScaleY(6.585f); + particle4->setRotation(-20); + particle4->setID("gauntlet-particles-4"_spr); + CCParticleSystemQuad* particle5 = GameToolbox::particleFromString("11a-1a1a0.3a8a90a90a0a0a0a0a0a0a0a0a0a0a50a3a0a0a0.239216a0a0.403922a0a0.419608a0a1a0a50a3a106a70a0.239216a0a0.403922a0a0.419608a0a1a0a0a0a0a0a0a0a0a0a0a0a0a2a1a0a0a0a176a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle5); + particle5->setPosition({0, 24}); + particle5->setZOrder(1); + particle5->setScaleX(1.710f); + particle5->setScaleY(1.710f); + particle5->setID("gauntlet-particles-5"_spr); + CCParticleSystemQuad* particle6 = GameToolbox::particleFromString("4a-1a1a0.3a3a90a90a0a0a0a0a0a0a0a0a0a0a50a3a0a0a0.32549a0a0.537255a0a0.560784a0a1a0a50a3a106a22a0.32549a0a0.537255a0a0.560784a0a1a0a0.3a0a0.3a0a0a0a0a0a0a0a0a2a1a0a0a0a190a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle6); + particle6->setPosition({-2, 24}); + particle6->setZOrder(1); + particle6->setScaleX(2.710f); + particle6->setScaleY(2.710f); + particle6->setID("gauntlet-particles-6"_spr); + } else if (gauntletName == "Cursed") { + CCSprite* sprite = CCSprite::create("cursed_gauntlet.png"_spr); + sprite->setPosition(bg->getPosition()); + sprite->setScale(1.270f/scaleFactor); + sprite->setZOrder(0); + sprite->setID("gauntlet-background"_spr); + node->addChild(sprite); + + // Show gauntlets sprite again + gauntlet->setVisible(true); + gauntlet->setPosition({1, 16}); + gauntlet->setScale(1); + gauntlet_node->setZOrder(3); + + // Particle + CCParticleSystemQuad* particle = GameToolbox::particleFromString("5a-1a1a0.3a3a90a90a29a0a11a0a0a0a0a0a0a0a50a1a24a45a0.105882a0a0.380392a0a0.105882a0a1a0a44a1a48a28a0.105882a0a0.380392a0a0.105882a0a1a0a0a0a0a0a0a0a0a0a0a0a1a2a1a0a0a0a175a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle); + particle->setPosition(gauntlet->getPosition()); + particle->setZOrder(1); + particle->setScaleX(2.035f); + particle->setScaleY(2.035f); + particle->setID("gauntlet-particles-1"_spr); + CCParticleSystemQuad* particle2 = GameToolbox::particleFromString("5a-1a1a0.3a3a90a90a29a0a11a0a0a0a0a0a0a0a50a1a24a45a0.105882a0a0.380392a0a0.105882a0a1a0a44a1a48a28a0.105882a0a0.380392a0a0.105882a0a1a0a0a0a0a0a0a0a0a0a0a0a1a2a1a0a0a0a175a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle2); + particle2->setPosition(gauntlet->getPosition()); + particle2->setZOrder(1); + particle2->setScaleX(2.035f); + particle2->setScaleY(2.035f); + particle2->setID("gauntlet-particles-2"_spr); + CCParticleSystemQuad* particle3 = GameToolbox::particleFromString("93a-1a1a0.3a71a90a0a52a0a10a0a23a0a0a0a0a0a2a1a0a0a0.00392157a0a0.905882a0a0a0a1a0a2a1a30a0a0.00392157a0a0.905882a0a0a0a1a0a0.3a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a32a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle3); + particle3->setPosition({17, 94}); + particle3->setZOrder(1); + particle3->setScaleX(4.510f); + particle3->setScaleY(3.335f); + particle3->setRotation(180); + particle3->setID("gauntlet-particles-3"_spr); + } else if (gauntletName == "Doom") { + CCSprite* sprite = CCSprite::create("doom_gauntlet.png"_spr); + sprite->setPosition(bg->getPosition()); + sprite->setScale(1.270f/scaleFactor); + sprite->setZOrder(0); + sprite->setID("gauntlet-background"_spr); + node->addChild(sprite); + + // Show gauntlets sprite again + gauntlet->setVisible(true); + gauntlet->setPosition({0, 19}); + gauntlet->setScale(1.025f); + gauntlet_node->setZOrder(3); + + // Particle + CCParticleSystemQuad* particle = GameToolbox::particleFromString("66a-1a3a0.3a20a90a27a11a0a43a83a0a0a0a0a0a0a5a1a0a0a0.733333a0a0.607843a0a0.12549a0a1a0a3a1a0a0a0.733333a0a0.470588a0a0.12549a0a1a0a0.5a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle); + particle->setPosition({0, -7}); + particle->setZOrder(1); + particle->setScaleX(1.235f); + particle->setScaleY(1.235f); + particle->setID("gauntlet-particles-1"_spr); + CCParticleSystemQuad* particle2 = GameToolbox::particleFromString("44a-1a3a0.3a13a90a27a11a0a32a62a0a0a0a0a0a0a50a1a0a0a0.0666667a0a0.0509804a0a0.0470588a0a1a0a50a1a0a0a0.0666667a0a0.0509804a0a0.0470588a0a1a0a0.5a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle2); + particle2->setPosition({0, -13}); + particle2->setZOrder(1); + particle2->setScaleX(1.310f); + particle2->setScaleY(1.435f); + particle2->setID("gauntlet-particles-2"_spr); + CCParticleSystemQuad* particle3 = GameToolbox::particleFromString("6a-1a1.9a0a3a90a17a69a0a3a0a0a-102a0a0a0a0a4a1a57a105a1a0a1a0a1a0a1a0a4a1a0a0a1a0a1a0a1a0a1a0a0a0a0a0a0a0a0a0a0a0a0a2a1a0a0a0a145a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle3); + particle3->setPosition(gauntlet->getPosition()); + particle3->setZOrder(1); + particle3->setScaleX(2.035f); + particle3->setScaleY(2.035f); + particle3->setID("gauntlet-particles-3"_spr); + } else if (gauntletName == "Split") { + CCSprite* sprite = CCSprite::create("split_gauntlet.png"_spr); + sprite->setPosition(bg->getPosition()); + sprite->setScale(1.270f/scaleFactor); + sprite->setZOrder(0); + sprite->setID("gauntlet-background"_spr); + node->addChild(sprite); + + // Show gauntlets sprite again + gauntlet->setVisible(true); + gauntlet->setPosition({-1, 20}); + gauntlet->setScale(0.975f); + gauntlet_node->setZOrder(2); + + // Particle + CCParticleSystemQuad* particle = GameToolbox::particleFromString("48a-1a2a0.3a20a90a2a19a0a10a20a0a0a0a0a0a0a4a1a0a0a0.666667a0a0.360784a0a0.603922a0a1a0a2a1a0a0a0.666667a0a0.360784a0a0.603922a0a1a0a0.5a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a31a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle); + particle->setPosition({-28, -65}); + particle->setZOrder(1); + particle->setScaleX(2.180f); + particle->setScaleY(4.003f); + particle->setID("gauntlet-particles-1"_spr); + CCParticleSystemQuad* particle2 = GameToolbox::particleFromString("48a-1a2a0.3a20a90a2a19a0a10a20a0a0a0a0a0a0a4a1a0a0a0.454902a0a0.215686a0a0.403922a0a1a0a2a1a0a0a0.454902a0a0.215686a0a0.403922a0a1a0a0.5a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle2); + particle2->setPosition({-28, -65}); + particle2->setZOrder(1); + particle2->setScaleX(2.180f); + particle2->setScaleY(4.003f); + particle2->setID("gauntlet-particles-2"_spr); + CCParticleSystemQuad* particle3 = GameToolbox::particleFromString("48a-1a2a0.3a20a90a2a19a0a10a20a0a0a0a0a0a0a4a1a0a0a0.243137a0a0.329412a0a0.341176a0a1a0a2a1a0a0a0.243137a0a0.329412a0a0.341176a0a1a0a0.5a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a31a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle3); + particle3->setPosition({25, 65}); + particle3->setZOrder(1); + particle3->setScaleX(2.180f); + particle3->setScaleY(4.003f); + particle3->setRotation(180); + particle3->setID("gauntlet-particles-3"_spr); + CCParticleSystemQuad* particle4 = GameToolbox::particleFromString("48a-1a2a0.3a20a90a2a19a0a10a20a0a0a0a0a0a0a4a1a0a0a0.145098a0a0.203922a0a0.207843a0a1a0a2a1a0a0a0.145098a0a0.203922a0a0.207843a0a1a0a0.5a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle4); + particle4->setPosition({25, 65}); + particle4->setZOrder(1); + particle4->setScaleX(2.180f); + particle4->setScaleY(4.003f); + particle4->setRotation(180); + particle4->setID("gauntlet-particles-4"_spr); + CCParticleSystemQuad* particle5 = GameToolbox::particleFromString("30a-1a2a0.3a13a90a90a29a0a11a0a0a0a0a0a0a0a6a1a0a0a0.705882a0a0.34902a0a0.698039a0a1a0a2a1a0a0a0.584314a0a0.34902a0a0.705882a0a1a0a0a0a0.5a0a6a0a34a0a0a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle5); + particle5->setPosition({0, 29}); + particle5->setZOrder(1); + particle5->setScaleX(1.955f); + particle5->setScaleY(1.955f); + particle5->setRotation(90); + particle5->setID("gauntlet-particles-5"_spr); + CCParticleSystemQuad* particle6 = GameToolbox::particleFromString("30a-1a2a0.3a13a90a90a29a0a11a0a0a0a0a0a0a0a6a1a0a0a0.254902a0a0.333333a0a0.388235a0a1a0a2a1a0a0a0.254902a0a0.333333a0a0.388235a0a1a0a0a0a0.5a0a6a0a34a0a0a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle6); + particle6->setPosition({0, 29}); + particle6->setZOrder(1); + particle6->setScaleX(1.955f); + particle6->setScaleY(1.955f); + particle6->setRotation(-90); + particle6->setID("gauntlet-particles-6"_spr); + CCParticleSystemQuad* particle7 = GameToolbox::particleFromString("30a-1a2a0.3a13a90a90a29a0a11a0a0a0a0a0a0a0a6a1a0a0a0.705882a0a0.34902a0a0.698039a0a1a0a2a1a0a0a0.584314a0a0.34902a0a0.705882a0a1a0a0a0a0.5a0a6a0a34a0a0a0a1a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle7); + particle7->setPosition({0, 29}); + particle7->setZOrder(1); + particle7->setScaleX(1.955f); + particle7->setScaleY(1.955f); + particle7->setRotation(90); + particle7->setID("gauntlet-particles-7"_spr); + CCParticleSystemQuad* particle8 = GameToolbox::particleFromString("30a-1a2a0.3a13a90a90a29a0a11a0a0a0a0a0a0a0a6a1a0a0a0.254902a0a0.333333a0a0.388235a0a1a0a2a1a0a0a0.254902a0a0.333333a0a0.388235a0a1a0a0a0a0.5a0a6a0a34a0a0a0a1a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle8); + particle8->setPosition({0, 29}); + particle8->setZOrder(1); + particle8->setScaleX(1.955f); + particle8->setScaleY(1.955f); + particle8->setRotation(-90); + particle8->setID("gauntlet-particles-8"_spr); + } else if (gauntletName == "Monster") { + CCSprite* sprite = CCSprite::create("monster_gauntlet.png"_spr); + sprite->setPosition(bg->getPosition()); + sprite->setScale(1.270f/scaleFactor); + sprite->setZOrder(0); + sprite->setID("gauntlet-background"_spr); + node->addChild(sprite); + + // Show gauntlets sprite again + gauntlet->setVisible(true); + gauntlet->setPosition({0, 22}); + gauntlet->setScale(1.075f); + gauntlet_node->setZOrder(2); + + // Particle + CCParticleSystemQuad* particle = GameToolbox::particleFromString("30a-1a3a0.3a9a90a9a11a0a17a40a0a0a0a0a0a0a4a1a0a0a0.0509804a0a0.929412a0a0a0a1a0a2a1a0a0a0.360784a0a0.486275a0a0.352941a0a1a0a0.1a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a165a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle); + particle->setPosition({gauntlet->getPositionX(), -37}); + particle->setZOrder(1); + particle->setScaleX(2.510f); + particle->setScaleY(1.985f); + particle->setID("gauntlet-particles-1"_spr); + CCParticleSystemQuad* particle2 = GameToolbox::particleFromString("66a-1a3a0.3a20a90a27a11a0a37a38a0a0a0a0a0a0a5a1a0a0a0.0156863a0a0.866667a0a0.027451a0a1a0a3a1a0a0a0.164706a0a0.509804a0a0a0a1a0a0.5a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle2); + particle2->setPosition({gauntlet->getPositionX(), -38}); + particle2->setZOrder(1); + particle2->setScaleX(1.072f); + particle2->setScaleY(2.475f); + particle2->setID("gauntlet-particles-2"_spr); + CCParticleSystemQuad* particle3 = GameToolbox::particleFromString("18a-1a3a0.3a5a90a9a11a0a32a28a0a0a0a0a0a0a50a1a0a0a0.0352941a0a0.0901961a0a0.0313726a0a1a0a50a1a0a0a0.0352941a0a0.0901961a0a0.0313726a0a1a0a1a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle3); + particle3->setPosition({gauntlet->getPositionX(), -48}); + particle3->setZOrder(1); + particle3->setScaleX(1.470f); + particle3->setScaleY(1.957f); + particle3->setID("gauntlet-particles-3"_spr); + } else if (gauntletName == "Mystery") { + CCSprite* sprite = CCSprite::create("mystery_gauntlet.png"_spr); + sprite->setPosition(bg->getPosition()); + sprite->setScale(1.270f/scaleFactor); + sprite->setZOrder(0); + sprite->setID("gauntlet-background"_spr); + node->addChild(sprite); + + // Show gauntlets sprite again + gauntlet->setVisible(true); + gauntlet->setPosition({3, 13}); + gauntlet->setScale(0.975f); + gauntlet_node->setZOrder(3); + + // Particle + CCParticleSystemQuad* particle = GameToolbox::particleFromString("30a-1a3a0.3a9a90a180a29a0a11a0a0a0a0a0a0a0a2a1a0a0a1a0a0.8a0a0.258824a0a1a0a8a1a0a0a1a0a0.8a0a0.258824a0a1a0a0.5a0a0.5a0a6a0a30a0a0a0a1a2a0a0a0a0a11a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle); + particle->setPosition(gauntlet->getPosition()); + particle->setZOrder(2); + particle->setScaleX(2.035f); + particle->setScaleY(2.035f); + particle->setID("gauntlet-particles-1"_spr); + CCParticleSystemQuad* particle2 = GameToolbox::particleFromString("31a-1a3a0.3a9a90a180a0a0a29a82a0a0a0a0a0a0a4a1a0a0a0.388235a0a0.741176a0a1a0a1a0a15a1a0a0a0.388235a0a0.741176a0a1a0a1a0a1a0a1a0a6a0a30a0a0a0a0a2a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle2); + particle2->setPosition({0, 0}); + particle2->setZOrder(1); + particle2->setScaleX(1.435f); + particle2->setScaleY(1.435f); + particle2->setID("gauntlet-particles-2"_spr); + } else if (gauntletName == "Strange") { + CCSprite* sprite = CCSprite::create("strange_gauntlet.png"_spr); + sprite->setPosition(bg->getPosition()); + sprite->setScale(1.270f/scaleFactor); + sprite->setZOrder(0); + sprite->setID("gauntlet-background"_spr); + node->addChild(sprite); + + reverseGauntlet(node); + + // Show gauntlets sprite again + gauntlet->setVisible(true); + gauntlet->setPosition({2, -18}); + gauntlet->setRotation(180); + gauntlet->setScale(0.975f); + gauntlet->setFlipX(true); + gauntlet_node->setZOrder(2); + + // Particle + CCParticleSystemQuad* particle = GameToolbox::particleFromString("36a-1a3a0.3a6a-90a24a9a0a11a0a0a-7a0a0a0a0a2a1a76a325a0.592157a0a0.294118a0a0.658824a0a1a0a2a1a201a149a0.592157a0a0.294118a0a0.658824a0a1a0a0.3a0a0.3a0a0a0a0a0a0a0a0a2a0a0a0a0a96a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle); + particle->setPosition({2, -65}); + particle->setZOrder(2); + particle->setScaleX(2.480f); + particle->setScaleY(2.728f); + particle->setRotation(180); + particle->setID("gauntlet-particles-1"_spr); + CCParticleSystemQuad* particle2 = GameToolbox::particleFromString("17a-1a4a0.3a3a180a0a8a0a47a34a0a0a0a0a0a0a50a1a0a0a0.243137a0a0.239216a0a0.239216a0a1a0a50a1a0a0a0.454902a0a0.447059a0a0.447059a0a1a0a1a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle2); + particle2->setPosition({17, 60}); + particle2->setZOrder(3); + particle2->setScaleX(0.805f); + particle2->setScaleY(1.155f); + particle2->setID("gauntlet-particles-2"_spr); + } else if (gauntletName == "Water") { + CCSprite* sprite = CCSprite::create("water_gauntlet.png"_spr); + sprite->setPosition(bg->getPosition()); + sprite->setScale(1.270f/scaleFactor); + sprite->setZOrder(0); + sprite->setID("gauntlet-background"_spr); + node->addChild(sprite); + } else if (gauntletName == "Fire") { + CCSprite* sprite = CCSprite::create("fire_gauntlet.png"_spr); + sprite->setPosition(bg->getPosition()); + sprite->setScale(1.270f/scaleFactor); + sprite->setZOrder(0); + sprite->setID("gauntlet-background"_spr); + node->addChild(sprite); + + // Show gauntlets sprite again + gauntlet->setVisible(true); + gauntlet->setPosition({0, 15}); + gauntlet->setScale(1.050f); + gauntlet_node->setZOrder(2); + + // Particle + CCParticleSystemQuad* particle = GameToolbox::particleFromString("30a-1a2a0.3a13a90a8a21a0a8a0a0a45a0a0a0a0a50a1a89a149a0.396078a0a0.27451a0a0.14902a0a1a0a31a1a145a68a0.141176a0a0.0352941a0a0.0313726a0a1a0a0.5a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle); + particle->setPosition({-1, 0}); + particle->setZOrder(1); + particle->setScaleX(1.435f); + particle->setScaleY(0.935f); + particle->setID("gauntlet-particles-1"_spr); + CCParticleSystemQuad* particle2 = GameToolbox::particleFromString("30a-1a1a0.3a13a90a8a21a0a0a0a0a45a0a0a0a0a21a1a89a149a0.894118a0a0.631373a0a0.345098a0a1a0a13a1a145a68a0.894118a0a0.631373a0a0.345098a0a1a0a0.5a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a183a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle2); + particle2->setPosition(gauntlet->getPosition()); + particle2->setZOrder(1); + particle2->setScaleX(2.322f); + particle2->setScaleY(1.347f); + particle2->setID("gauntlet-particles-2"_spr); + CCParticleSystemQuad* particle3 = GameToolbox::particleFromString("19a-1a2a0.3a8a90a0a29a0a5a0a0a0a0a0a0a0a50a1a50a33a0.12549a0a0.0862745a0a0a0a1a0a30a1a50a30a0.12549a0a0.0862745a0a0a0a1a0a0.1a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a181a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle3); + particle3->setPosition(gauntlet->getPosition()); + particle3->setZOrder(1); + particle3->setScaleX(1.145f); + particle3->setScaleY(1.795f); + particle3->setID("gauntlet-particles-3"_spr); + CCParticleSystemQuad* particle4 = GameToolbox::particleFromString("31a-1a3a0.3a9a90a27a11a0a37a38a0a0a0a0a0a0a50a1a0a0a0.133333a0a0.0862745a0a0.0588235a0a1a0a50a1a0a0a0.0666667a0a0.0470588a0a0.0352941a0a1a0a0.5a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle4); + particle4->setPosition({1, -23}); + particle4->setZOrder(1); + particle4->setScaleX(1.270f); + particle4->setScaleY(1.832f); + particle4->setID("gauntlet-particles-4"_spr); + CCParticleSystemQuad* particle5 = GameToolbox::particleFromString("40a-1a3a0.3a12a90a13a11a0a48a78a0a0a0a0a0a0a5a3a0a0a0.439216a0a0.309804a0a0.129412a0a1a0a3a3a0a0a0.439216a0a0.309804a0a0.129412a0a1a0a0.1a0a0.3a0a0a0a0a0a0a0a0a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle5); + particle5->setPosition({2, -20}); + particle5->setZOrder(1); + particle5->setScaleX(1.205f); + particle5->setScaleY(1.280f); + particle5->setID("gauntlet-particles-5"_spr); + } else if (gauntletName == "Ice") { + CCSprite* sprite = CCSprite::create("ice_gauntlet.png"_spr); + sprite->setPosition(bg->getPosition()); + sprite->setScale(1.270f/scaleFactor); + sprite->setZOrder(0); + sprite->setID("gauntlet-background"_spr); + node->addChild(sprite); + + // Show gauntlets sprite again + gauntlet->setVisible(true); + gauntlet->setPosition({0, 15}); + gauntlet->setScale(1.050f); + gauntlet_node->setZOrder(2); + + // Particle + CCParticleSystemQuad* particle = GameToolbox::particleFromString("25a-1a1a0.3a19a90a90a0a0a0a0a0a0a0a0a0a0a12a1a0a98a0.0980392a0a0.176471a0a0.219608a0a1a0a36a1a0a0a0.0980392a0a0.176471a0a0.219608a0a1a0a0a0a0a0a0a0a0a0a0a0a0a2a1a0a0a0a191a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle); + particle->setPosition({0, 17}); + particle->setZOrder(1); + particle->setScaleX(3.760f); + particle->setScaleY(3.760f); + particle->setID("gauntlet-particles-1"_spr); + CCParticleSystemQuad* particle2 = GameToolbox::particleFromString("200a-1a4a0.3a46a90a0a18a0a24a0a0a0a0a0a0a0a1a1a0a39a0.980392a0a1a0a0.980392a0a1a0a1a1a98a41a0.980392a0a1a0a0.980392a0a1a0a0.3a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a14a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle2); + particle2->setPosition({11, 112}); + particle2->setZOrder(1); + particle2->setScaleX(2.322f); + particle2->setScaleY(3.097f); + particle2->setRotation(-174); + particle2->setID("gauntlet-particles-2"_spr); + CCParticleSystemQuad* particle3 = GameToolbox::particleFromString("44a-1a3a0.3a13a90a27a11a0a40a62a0a0a0a0a0a0a50a1a0a0a0.0431373a0a0.0627451a0a0.0666667a0a1a0a50a1a0a0a0.0431373a0a0.0627451a0a0.0666667a0a1a0a0.5a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle3); + particle3->setPosition({0, -11}); + particle3->setZOrder(1); + particle3->setScaleX(1.145f); + particle3->setScaleY(1.795f); + particle3->setID("gauntlet-particles-3"_spr); + CCParticleSystemQuad* particle4 = GameToolbox::particleFromString("38a-1a1.5a0.3a21a90a90a0a0a31a69a0a0a0a0a0a0a2a1a65a57a1a0a1a0a1a0a1a0a5a1a0a0a1a0a1a0a1a0a1a0a0.2a0a0.4a0a0a0a0a0a0a0a0a2a1a0a0a0a169a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle4); + particle4->setPosition(gauntlet->getPosition()); + particle4->setZOrder(1); + particle4->setScaleX(1.470f); + particle4->setScaleY(1.957f); + particle4->setID("gauntlet-particles-4"_spr); + } else if (gauntletName == "Poison") { + CCSprite* sprite = CCSprite::create("poison_gauntlet.png"_spr); + sprite->setPosition(bg->getPosition()); + sprite->setScale(1.270f/scaleFactor); + sprite->setZOrder(0); + sprite->setID("gauntlet-background"_spr); + node->addChild(sprite); + + // Particle + CCParticleSystemQuad* particle = GameToolbox::particleFromString("31a-1a3a0.3a9a90a27a11a0a37a64a0a0a0a0a0a0a50a1a0a0a0.054902a0a0.129412a0a0.0627451a0a1a0a50a1a0a0a0.054902a0a0.129412a0a0.0627451a0a1a0a0.5a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle); + particle->setPosition({0, 11}); + particle->setZOrder(1); + particle->setScaleX(1.260f); + particle->setScaleY(1.485f); + particle->setID("gauntlet-particles-1"_spr); + CCParticleSystemQuad* particle2 = GameToolbox::particleFromString("20a-1a3a0.3a6a90a22a11a0a33a55a0a0a0a0a0a0a1a1a0a0a0.137255a0a0.301961a0a0.133333a0a1a0a10a1a0a0a0.137255a0a0.301961a0a0.133333a0a1a0a0.1a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a40a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle2); + particle2->setPosition({-3, -13}); + particle2->setZOrder(1); + particle2->setScaleX(1.722f); + particle2->setScaleY(1.397f); + particle2->setID("gauntlet-particles-2"_spr); + CCParticleSystemQuad* particle3 = GameToolbox::particleFromString("4a-1a2.5a0.3a1a-90a0a0a10a35a0a0a-70a0a0a0a0a6a2a140a0a0.345098a0a0.690196a0a0.301961a0a1a0a4a2a140a0a0.345098a0a0.690196a0a0.301961a0a1a0a0a0a0a0a0a0a0a0a0a0a0a2a1a0a0a0a77a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle3); + particle3->setPosition({0, 109}); + particle3->setZOrder(1); + particle3->setScaleX(1.145f); + particle3->setScaleY(1.795f); + particle3->setID("gauntlet-particles-3"_spr); + CCParticleSystemQuad* particle4 = GameToolbox::particleFromString("40a-1a3a0.3a12a90a13a11a0a48a78a0a0a0a0a0a0a3a1a0a0a0.129412a0a0.439216a0a0.168627a0a1a0a1a1a0a0a0.129412a0a0.439216a0a0.168627a0a1a0a0.1a0a0.3a0a0a0a0a0a0a0a0a2a1a0a0a0a192a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle4); + particle4->setPosition(gauntlet->getPosition()); + particle4->setZOrder(1); + particle4->setScaleX(1.270f); + particle4->setScaleY(1.632f); + particle4->setID("gauntlet-particles-4"_spr); + } else if (gauntletName == "Shadow") { + CCSprite* sprite = CCSprite::create("shadow_gauntlet.png"_spr); + sprite->setPosition(bg->getPosition()); + sprite->setScale(1.745f/scaleFactor); + sprite->setZOrder(0); + sprite->setID("gauntlet-background"_spr); + node->addChild(sprite); + + // Show gauntlets sprite again + gauntlet->setVisible(true); + gauntlet->setPosition({2, 26}); + gauntlet->setScale(0.975f); + gauntlet_node->setZOrder(2); + + // Particle + CCParticleSystemQuad* particle = GameToolbox::particleFromString("48a-1a3a0.3a14a90a27a6a0a40a57a0a0a0a0a0a0a50a1a0a0a0.0509804a0a0.0235294a0a0.0666667a0a1a0a50a1a0a0a0.0509804a0a0.0235294a0a0.0666667a0a1a0a0.5a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle); + particle->setPosition({0, -15}); + particle->setZOrder(1); + particle->setScaleX(1.310f); + particle->setScaleY(1.735f); + particle->setID("gauntlet-particles-1"_spr); + CCParticleSystemQuad* particle2 = GameToolbox::particleFromString("62a-1a1a0.3a47a90a90a29a0a11a0a0a0a0a0a0a0a50a1a0a59a0.0784314a0a0.0235294a0a0.0941176a0a1a0a38a1a103a65a0.0784314a0a0.0235294a0a0.0941176a0a1a0a0.5a0a0.5a0a0a0a0a0a0a0a1a2a1a0a0a0a190a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle2); + particle2->setPosition({0, 25}); + particle2->setZOrder(1); + particle2->setScaleX(2.472f); + particle2->setScaleY(2.472f); + particle2->setID("gauntlet-particles-2"_spr); + CCParticleSystemQuad* particle3 = GameToolbox::particleFromString("62a-1a1a0.3a47a90a180a29a0a11a0a0a0a0a0a0a0a50a1a0a0a0.0313726a0a0.00392157a0a0.0470588a0a1a0a38a1a0a0a0.0313726a0a0.00392157a0a0.0470588a0a1a0a0.5a0a0.5a0a38a0a9a0a-85a26a1a2a1a0a0a0a183a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle3); + particle3->setPosition({0, 25}); + particle3->setZOrder(1); + particle3->setScaleX(1.470f); + particle3->setScaleY(1.470f); + particle3->setID("gauntlet-particles-3"_spr); + CCParticleSystemQuad* particle4 = GameToolbox::particleFromString("62a-1a1a0.3a47a90a180a29a0a11a0a0a0a0a0a0a0a50a1a0a0a0.0313726a0a0.00392157a0a0.0431373a0a1a0a38a1a0a0a0.0313726a0a0.00392157a0a0.0431373a0a1a0a0.5a0a0.5a0a38a0a9a0a-85a26a1a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle4); + particle4->setPosition({0, 25}); + particle4->setZOrder(1); + particle4->setScaleX(1.195f); + particle4->setScaleY(1.195f); + particle4->setID("gauntlet-particles-4"_spr); + CCParticleSystemQuad* particle5 = GameToolbox::particleFromString("40a-1a3a0.3a12a90a13a11a0a48a78a0a0a0a0a0a0a5a3a0a0a0.129412a0a0.439216a0a0.133333a0a1a0a3a3a0a0a0.129412a0a0.439216a0a0.133333a0a1a0a0.1a0a0.3a0a0a0a0a0a0a0a0a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle5); + particle5->setPosition({0, -16}); + particle5->setZOrder(1); + particle5->setScaleX(1.255f); + particle5->setScaleY(1.430f); + particle5->setID("gauntlet-particles-5"_spr); + } else if (gauntletName == "Lava") { + CCSprite* sprite = CCSprite::create("lava_gauntlet.png"_spr); + sprite->setPosition(bg->getPosition()); + sprite->setScale(1.270f/scaleFactor); + sprite->setZOrder(0); + sprite->setID("gauntlet-background"_spr); + node->addChild(sprite); + + // Show gauntlets sprite again + gauntlet->setVisible(true); + gauntlet->setPosition({0, 16}); + gauntlet->setScale(0.950f); + gauntlet_node->setZOrder(2); + + // Particle + CCParticleSystemQuad* particle = GameToolbox::particleFromString("40a-1a3a0.3a12a90a13a11a0a48a78a0a0a0a0a0a0a5a3a0a0a0.439216a0a0.309804a0a0.129412a0a1a0a3a3a0a0a0.439216a0a0.309804a0a0.129412a0a1a0a0.1a0a0.3a0a0a0a0a0a0a0a0a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle); + particle->setPosition({0, -16}); + particle->setZOrder(1); + particle->setScaleX(1.360f); + particle->setScaleY(1.360f); + particle->setID("gauntlet-particles-1"_spr); + CCParticleSystemQuad* particle2 = GameToolbox::particleFromString("59a-1a1a0.3a45a90a14a100a54a2a6a0a-135a0a46a0a0a20a8a0a0a1a0a0.756863a0a0.258824a0a1a0a1a1a0a0a1a0a0.694118a0a0.321569a0a1a0a0a0a0.1a0a0a0a0a0a0a0a0a2a1a0a0a1a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle2); + particle2->setPosition(gauntlet->getPosition()); + particle2->setZOrder(1); + particle2->setScaleX(1.522f); + particle2->setScaleY(1.547f); + particle2->setID("gauntlet-particles-2"_spr); + CCParticleSystemQuad* particle3 = GameToolbox::particleFromString("56a-1a1a0.3a43a90a16a103a81a2a6a0a-180a0a46a0a0a13a8a0a0a1a0a0.756863a0a0.258824a0a1a0a1a1a0a0a1a0a0.694118a0a0.321569a0a1a0a0a0a0.1a0a0a0a0a0a0a0a0a2a1a0a0a1a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle3); + particle3->setPosition(gauntlet->getPosition()); + particle3->setZOrder(1); + particle3->setScaleX(1.470f); + particle3->setScaleY(1.370f); + particle3->setID("gauntlet-particles-3"_spr); + CCParticleSystemQuad* particle4 = GameToolbox::particleFromString("56a-1a1a0.5a19a90a28a49a43a2a6a0a23a0a43a0a0a50a33a0a0a0.172549a0a0.160784a0a0.14902a0a0.47a0a50a38a0a0a0.247059a0a0.243137a0a0.239216a0a0.47a0a0a0a0.13a0a0a0a0a0a0a0a0a2a0a0a0a1a29a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle4); + particle4->setPosition(gauntlet->getPosition()); + particle4->setZOrder(1); + particle4->setScaleX(1.095f); + particle4->setScaleY(0.845f); + particle4->setID("gauntlet-particles-4"_spr); + CCParticleSystemQuad* particle5 = GameToolbox::particleFromString("59a-1a1a0.3a45a90a18a113a30a2a6a0a-192a0a46a0a0a20a8a0a0a1a0a0.505882a0a0.258824a0a1a0a1a1a0a0a1a0a0.505882a0a0.258824a0a1a0a0a0a0.1a0a0a0a0a0a0a0a0a2a1a0a0a1a158a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle5); + particle5->setPosition(gauntlet->getPosition()); + particle5->setZOrder(1); + particle5->setScaleX(1.255f); + particle5->setScaleY(1.430f); + particle5->setID("gauntlet-particles-5"_spr); + CCParticleSystemQuad* particle6 = GameToolbox::particleFromString("48a-1a3a0.3a14a90a27a6a0a40a57a0a0a0a0a0a0a50a1a0a0a0.0666667a0a0.0352941a0a0.0196078a0a1a0a50a1a0a0a0.0666667a0a0.0352941a0a0.0196078a0a1a0a0.5a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle6); + particle6->setPosition({0, -18}); + particle6->setZOrder(1); + particle6->setScaleX(1.235f); + particle6->setScaleY(1.835f); + particle6->setID("gauntlet-particles-6"_spr); + } else if (gauntletName == "Time") { + CCSprite* sprite = CCSprite::create("time_gauntlet.png"_spr); + sprite->setPosition(bg->getPosition()); + sprite->setScale(1.220f/scaleFactor); + sprite->setZOrder(0); + sprite->setID("gauntlet-background"_spr); + node->addChild(sprite); + } else if (gauntletName == "Spike") { + CCSprite* sprite = CCSprite::create("spike_gauntlet.png"_spr); + sprite->setPosition(bg->getPosition()); + sprite->setScale(1.920f/scaleFactor); + sprite->setZOrder(0); + sprite->setID("gauntlet-background"_spr); + node->addChild(sprite); + } else if (gauntletName == "Cyborg") { + CCSprite* sprite = CCSprite::create("cyborg_gauntlet.png"_spr); + sprite->setPosition(bg->getPosition()); + sprite->setScale(1.270f/scaleFactor); + sprite->setZOrder(0); + sprite->setID("gauntlet-background"_spr); + node->addChild(sprite); + } else if (gauntletName == "Inferno") { + CCSprite* sprite = CCSprite::create("inferno_gauntlet.png"_spr); + sprite->setPosition(bg->getPosition()); + sprite->setScale(1.270f/scaleFactor); + sprite->setZOrder(0); + sprite->setID("gauntlet-background"_spr); + node->addChild(sprite); + } else if (gauntletName == "Death") { + CCSprite* sprite = CCSprite::create("death_gauntlet.png"_spr); + sprite->setPosition(bg->getPosition()); + sprite->setScale(1.270f/scaleFactor); + sprite->setZOrder(0); + sprite->setID("gauntlet-background"_spr); + node->addChild(sprite); + } else if (gauntletName == "Magic") { + CCSprite* sprite = CCSprite::create("magic_gauntlet.png"_spr); + sprite->setPosition(bg->getPosition()); + sprite->setScale(1.270f/scaleFactor); + sprite->setZOrder(0); + sprite->setID("gauntlet-background"_spr); + node->addChild(sprite); + } else if (gauntletName == "Chaos") { + CCSprite* sprite = CCSprite::create("chaos_gauntlet.png"_spr); + sprite->setPosition(bg->getPosition()); + sprite->setScale(1.270f/scaleFactor); + sprite->setZOrder(0); + sprite->setID("gauntlet-background"_spr); + node->addChild(sprite); + } else if (gauntletName == "Force") { + CCSprite* sprite = CCSprite::create("force_gauntlet.png"_spr); + sprite->setPosition(bg->getPosition()); + sprite->setScale(1.270f/scaleFactor); + sprite->setZOrder(0); + sprite->setID("gauntlet-background"_spr); + node->addChild(sprite); + } else if (gauntletName == "NCS I") { + CCSprite* sprite = CCSprite::create("ncs_gauntlet_1.png"_spr); + sprite->setPosition(bg->getPosition()); + sprite->setScale(1.270f/scaleFactor); + sprite->setZOrder(0); + sprite->setID("gauntlet-background"_spr); + node->addChild(sprite); + + // Show gauntlets sprite again + gauntlet->setVisible(true); + gauntlet->setPosition({0, 16}); + gauntlet->setScale(0.9f); + gauntlet_node->setZOrder(3); + + CCParticleSystemQuad* particle = GameToolbox::particleFromString("25a-1a1a0.3a19a90a90a0a0a0a0a0a0a0a0a0a0a23a1a0a98a0.0980392a0a0.176471a0a0.219608a0a1a0a50a1a0a0a0.0980392a0a0.176471a0a0.219608a0a1a0a0a0a0a0a0a0a0a0a0a0a0a2a1a0a0a0a185a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle); + particle->setPosition({0, 20}); + particle->setZOrder(2); + particle->setScale(2.060f); + particle->setID("gauntlet-particles-1"_spr); + + CCParticleSystemQuad* particle2 = GameToolbox::particleFromString("25a-1a1a0.3a19a90a90a0a0a0a0a0a0a0a0a0a0a12a1a0a98a0.0980392a0a0.176471a0a0.219608a0a1a0a36a1a0a0a0.0980392a0a0.176471a0a0.219608a0a1a0a0a0a0a0a0a0a0a0a0a0a0a2a1a0a0a0a167a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle2); + particle2->setPosition(gauntlet->getPosition()); + particle2->setZOrder(2); + particle2->setScale(4.210f); + particle2->setID("gauntlet-particles-2"_spr); + + CCParticleSystemQuad* particle3 = GameToolbox::particleFromString("30a-1a2.5a0.3a10a90a12a19a0a23a0a0a11a0a0a0a0a4a1a0a0a0.313726a0a0.458824a0a0.529412a0a1a0a2a1a0a0a0.313726a0a0.458824a0a0.529412a0a1a0a0a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a19a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle3); + particle3->setZOrder(0); + particle3->setPosition({0, -81}); + particle3->setScale(2.035f); + particle3->setID("gauntlet-particles-3"_spr); + } else if (gauntletName == "NCS II") { + CCSprite* sprite = CCSprite::create("ncs_gauntlet_2.png"_spr); + sprite->setPosition(bg->getPosition()); + sprite->setScale(1.270f/scaleFactor); + sprite->setZOrder(0); + sprite->setID("gauntlet-background"_spr); + node->addChild(sprite); + + // Show gauntlets sprite again + gauntlet->setVisible(true); + gauntlet->setPosition({0, 16}); + gauntlet->setScale(0.9f); + gauntlet_node->setZOrder(3); + + CCParticleSystemQuad* particle = GameToolbox::particleFromString("25a-1a1a0.3a19a90a90a0a0a0a0a0a0a0a0a0a0a23a1a0a98a0.301961a0a0.156863a0a0.341176a0a1a0a50a1a0a0a0.301961a0a0.156863a0a0.341176a0a1a0a0a0a0a0a0a0a0a0a0a0a0a2a1a0a0a0a185a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle); + particle->setPosition({0, 20}); + particle->setZOrder(2); + particle->setScale(2.060f); + particle->setID("gauntlet-particles-1"_spr); + + CCParticleSystemQuad* particle2 = GameToolbox::particleFromString("25a-1a1a0.3a19a90a90a0a0a0a0a0a0a0a0a0a0a12a1a0a98a0.301961a0a0.156863a0a0.341176a0a1a0a36a1a0a0a0.301961a0a0.156863a0a0.341176a0a1a0a0a0a0a0a0a0a0a0a0a0a0a2a1a0a0a0a167a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle2); + particle2->setPosition(gauntlet->getPosition()); + particle2->setZOrder(2); + particle2->setScale(4.210f); + particle2->setID("gauntlet-particles-2"_spr); + + CCParticleSystemQuad* particle3 = GameToolbox::particleFromString("30a-1a2.5a0.3a10a90a12a19a0a23a0a0a11a0a0a0a0a4a1a0a0a0.482353a0a0.317647a0a0.533333a0a1a0a2a1a0a0a0.482353a0a0.317647a0a0.533333a0a1a0a0a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a19a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); + node->addChild(particle3); + particle3->setZOrder(0); + particle3->setPosition({0, -81}); + particle3->setScale(2.035f); + particle3->setID("gauntlet-particles-3"_spr); + } else if (gauntletName == "Discord") { + CCSprite* sprite = CCSprite::create("discord_gauntlet.png"_spr); + sprite->setPosition({bg->getPositionX(), bg->getPositionY() + 3}); + sprite->setScale(1.245f/scaleFactor); + sprite->setZOrder(0); + item->setZOrder(1); + sprite->setID("gauntlet-background"_spr); + node->addChild(sprite); + } else if (gauntletName == "Space") { + CCSprite* sprite = CCSprite::create("space_gauntlet.png"_spr); + sprite->setPosition({bg->getPositionX(), bg->getPositionY() + 3}); + sprite->setScale(1.245f/scaleFactor); + sprite->setZOrder(0); + item->setZOrder(1); + sprite->setID("gauntlet-background"_spr); + node->addChild(sprite); + } else if (gauntletName == "Cosmos") { + CCSprite* sprite = CCSprite::create("cosmos_gauntlet.png"_spr); + sprite->setPosition({bg->getPositionX(), bg->getPositionY() + 3}); + sprite->setScale(1.245f/scaleFactor); + sprite->setZOrder(0); + item->setZOrder(1); + sprite->setID("gauntlet-background"_spr); + node->addChild(sprite); + } else { + bg->setVisible(true); + label->setVisible(true); + label_shadow->setVisible(true); + label_gauntlet->setVisible(true); + label_shadow_gauntlet->setVisible(true); + gauntlet->setVisible(true); + gauntlet_shadow->setVisible(true); + } + } + } + } +}; + +static void toggleHook(bool value) { + for (auto& hook : geode::Mod::get()->getHooks()) { + if (hook->getDisplayName().starts_with("GauntletSelectLayer")) { + if (value) { + (void)hook->enable(); + } else { + (void)hook->disable(); + } + } + } +} + +$execute { + listenForSettingChanges("gauntletDesign", [](bool value) { + log::info("Gauntlet Design changed to {}", value); + toggleHook(value); + }); +} + +$on_mod(Loaded) { + if (!Mod::get()->getSettingValue("gauntletDesign")) { + toggleHook(false); + } +} diff --git a/src/hooks/InfoLayer.cpp b/src/hooks/InfoLayer.cpp new file mode 100644 index 0000000..19a4164 --- /dev/null +++ b/src/hooks/InfoLayer.cpp @@ -0,0 +1,45 @@ +#include +#include + +// Copy button for sharing levels +class $modify(ProtoInfoLayer, InfoLayer) { + bool init(GJGameLevel* level, GJUserScore* userscore, GJLevelList* levellist) { + if (!InfoLayer::init(level, userscore, levellist)) return false; + if (level == nullptr) return true; + std::string levelID = std::to_string(level->m_levelID.value()); + if (auto menu = typeinfo_cast(this->getChildByIDRecursive("left-side-menu"))) { + Build::createSpriteName("GJ_redoBtn_001.png").scale(0.7f).intoMenuItem([levelID = std::move(levelID)]() { + utils::clipboard::write(fmt::format("https://gdutils.com/{}", levelID)); + auto notification = Notification::create("Copied level URL to clipboard!", NotificationIcon::Success); + notification->setTime(0.2f); + notification->show(); + }).pos(14, -55).parent(menu); + } + return true; + } +}; + +static void toggleHook(bool value) { + for (auto& hook : geode::Mod::get()->getHooks()) { + if (hook->getDisplayName() == "InfoLayer::init") { + if (value) { + (void)hook->enable(); + } else { + (void)hook->disable(); + } + } + } +} + +$execute { + listenForSettingChanges("levelsProtocolButton", [](bool value) { + log::info("Level Protocols Button changed to {}", value); + toggleHook(value); + }); +} + +$on_mod(Loaded) { + if (!Mod::get()->getSettingValue("levelsProtocolButton")) { + toggleHook(false); + } +} diff --git a/src/hooks/LevelInfoLayer.cpp b/src/hooks/LevelInfoLayer.cpp index c1dab01..65378d2 100644 --- a/src/hooks/LevelInfoLayer.cpp +++ b/src/hooks/LevelInfoLayer.cpp @@ -6,7 +6,7 @@ // demon list std::unordered_map demonListCache; // Will clear after game exit, or if user deletes level -class $modify(LevelInfoLayer) { +class $modify(CustomLevelInfoLayer, LevelInfoLayer) { struct Fields { async::TaskHolder m_listener; }; @@ -149,7 +149,7 @@ class $modify(LevelInfoLayer) { }; // demon list -void toggleHook(bool value) { +static void toggleHook(bool value) { for (auto& hook : geode::Mod::get()->getHooks()) { if (hook->getDisplayName() == "LevelInfoLayer::init") { if (value) { diff --git a/src/hooks/SecretLayer.cpp b/src/hooks/SecretLayer.cpp new file mode 100644 index 0000000..0f6586e --- /dev/null +++ b/src/hooks/SecretLayer.cpp @@ -0,0 +1,44 @@ +#include +#include + +class $modify(SecretLayer) { + bool init() { + if (!SecretLayer::init()) return false; + auto winSize = CCDirector::sharedDirector()->getWinSize(); + auto bg = CCSprite::createWithSpriteFrameName("thevault.png"_spr); + bg->setPosition(winSize / 2); + bg->setZOrder(-1); + bg->setScaleY(winSize.height / bg->getContentSize().height); + bg->setScaleX(winSize.width / bg->getContentSize().width); + this->addChild(bg); + if (auto vaultName = this->getChildByID("vault-name")) { + vaultName->setVisible(false); + } + return true; + } +}; + +static void toggleHook(bool value) { + for (auto& hook : geode::Mod::get()->getHooks()) { + if (hook->getDisplayName() == "SecretLayer::init") { + if (value) { + (void)hook->enable(); + } else { + (void)hook->disable(); + } + } + } +} + +$execute { + listenForSettingChanges("secretLayerDesign", [](bool value) { + log::info("SecretLayer Design changed to {}", value); + toggleHook(value); + }); +} + +$on_mod(Loaded) { + if (!Mod::get()->getSettingValue("secretLayerDesign")) { + toggleHook(false); + } +} diff --git a/src/ui/DownloadPopup.cpp b/src/ui/DownloadPopup.cpp index 5d24fcb..81e74d2 100644 --- a/src/ui/DownloadPopup.cpp +++ b/src/ui/DownloadPopup.cpp @@ -27,12 +27,12 @@ void RobProgressBar::setProgress(float percentage) { } RobProgressBar* RobProgressBar::create() { - auto pRet = new RobProgressBar(); - if (pRet && pRet->init()) { - pRet->autorelease(); - return pRet; + auto ret = new RobProgressBar(); + if (ret && ret->init()) { + ret->autorelease(); + return ret; } - CC_SAFE_DELETE(pRet); + delete ret; return nullptr; } diff --git a/src/ui/EventInfoPopup.cpp b/src/ui/EventInfoPopup.cpp new file mode 100644 index 0000000..e669d0d --- /dev/null +++ b/src/ui/EventInfoPopup.cpp @@ -0,0 +1,69 @@ +#include "EventInfoPopup.hpp" + +EventInfoPopup* EventInfoPopup::create() { + auto ret = new EventInfoPopup(); + if (ret && ret->init()) { + ret->autorelease(); + return ret; + } + delete ret; + return nullptr; +} + +arc::Future> EventInfoPopup::reqTask() { + ARC_FRAME(); + auto req = web::WebRequest(); + req.header("Cache-Control", "no-cache").header("Pragma", "no-cache"); + + std::string URL = "https://clarifygdps.com/gdutils/event_info/image_banner.png?randome=" + std::to_string(static_cast(time(nullptr))); + //std::string URL = "https://clarifygdps.com/gdutils/event_info/image_banner_oldNCS.png"; + + auto response = co_await req.get(URL); + + if (response.ok()) { + co_return Ok(std::move(response).data()); + } + + co_return Err(response.errorMessage()); +} + +bool EventInfoPopup::init() { + if (!Popup::init(400.0f, 225.0f, "GJ_square02.png")) return false; + + m_sprite = LazySprite::create(this->m_mainLayer->getContentSize()); + m_sprite->setScale(.55f); + this->m_mainLayer->addChildAtPosition(m_sprite, Anchor::Center); + + m_sprite->setLoadCallback([this](Result<> res) { + this->imageCreationFinished(std::move(res)); + }); + + m_listener.spawn(EventInfoPopup::reqTask(), [this](auto result) { + onFetch(std::move(result)); + }); + return true; +} + +void EventInfoPopup::imageCreationFinished(Result<> res) { + if (!res) { + log::debug("Failed to load image: {}", res.err().value_or(std::string{})); + this->onDownloadFail(); + return; + } +} + +void EventInfoPopup::onDownloadFail() { + auto failLabel = CCLabelBMFont::create("No event banner found.", "bigFont.fnt", 0.0f, kCCTextAlignmentCenter); + m_sprite->cancelLoad(); + failLabel->setScale(0.7f); + this->m_mainLayer->addChildAtPosition(failLabel, Anchor::Center); +} + +void EventInfoPopup::onFetch(Result result) { + if (result.isErr()) { + log::error("Error when fetching image: {}", result.unwrapErr()); + this->onDownloadFail(); + } else { + m_sprite->loadFromData(std::move(result).unwrap()); + } +} diff --git a/src/ui/EventInfoPopup.hpp b/src/ui/EventInfoPopup.hpp new file mode 100644 index 0000000..bc71d64 --- /dev/null +++ b/src/ui/EventInfoPopup.hpp @@ -0,0 +1,22 @@ +#pragma once +#include + +class EventInfoPopup : public Popup { + protected: + LazySprite* m_sprite; + async::TaskHolder> m_listener; + Ref m_image; + + bool init() override; + void loadPage(); + void imageCreationFinished(Result<>); + void onDownloadFail(); + arc::Future> reqTask(); + void onFetch(Result); + public: + static EventInfoPopup* create(); + virtual void onClose(CCObject*) override { + m_listener.cancel(); + Popup::onClose(nullptr); + } +}; diff --git a/src/ui/SearchUserLayer.cpp b/src/ui/SearchUserLayer.cpp new file mode 100644 index 0000000..db8884d --- /dev/null +++ b/src/ui/SearchUserLayer.cpp @@ -0,0 +1,14 @@ +#include "SearchUserLayer.hpp" + +bool SearchUserLayer::init(FriendsProfilePage* layer) { + //if (!Popup::init(260.f, 60.f)) return false; + if (!Popup::init(260.f, 120.f)) return false; + m_friendPage = layer; + input_username->setMaxCharCount(20); + input_username->setPlaceholder(""); + m_buttonMenu->addChildAtPosition(input_username, Anchor::Center, {0, 10}); + Build::create("Search", 60, true, "bigFont.fnt", "GJ_button_01.png", 30, .5F).intoMenuItem([this]() { + onValidate(); + }).parentAtPos(m_buttonMenu, Anchor::Center, {0, -35}); + return true; +} diff --git a/src/ui/SearchUserLayer.hpp b/src/ui/SearchUserLayer.hpp new file mode 100644 index 0000000..2b6f243 --- /dev/null +++ b/src/ui/SearchUserLayer.hpp @@ -0,0 +1,22 @@ +#pragma once +#include + +class SearchUserLayer : public Popup { + protected: + WeakRef m_friendPage; + + bool init(FriendsProfilePage*); + void onValidate(); + public: + TextInput* input_username = TextInput::create(200.0F, "Username", "bigFont.fnt"); + static SearchUserLayer* create(FriendsProfilePage* layer) { + auto ret = new SearchUserLayer(); + if (ret->init(layer)) { + ret->autorelease(); + return ret; + } + delete ret; + return nullptr; + } +}; + From 2ebbb75af903eb001ec6cfea78b2277e77710f26 Mon Sep 17 00:00:00 2001 From: FireMario211 <17692105+FireMario211@users.noreply.github.com> Date: Sat, 21 Feb 2026 17:13:26 -0500 Subject: [PATCH 04/32] mod.json --- mod.json | 25 +++--- src/modules/events/EventHandler.cpp | 13 --- src/modules/events/EventHandler.hpp | 118 --------------------------- src/ui/OldBorder.cpp | 119 ++++++++++++++++++++++++++++ src/ui/OldBorder.hpp | 44 ++++++++++ 5 files changed, 179 insertions(+), 140 deletions(-) delete mode 100644 src/modules/events/EventHandler.cpp delete mode 100644 src/modules/events/EventHandler.hpp create mode 100644 src/ui/OldBorder.cpp create mode 100644 src/ui/OldBorder.hpp diff --git a/mod.json b/mod.json index b915767..9e35d04 100644 --- a/mod.json +++ b/mod.json @@ -1,11 +1,11 @@ { - "geode": "4.9.0", + "geode": "5.0.0-beta.2", "version": "v1.5.4", "gd": { - "win": "2.2074", - "mac": "2.2074", - "android": "2.2074", - "ios": "2.2074" + "win": "2.2081", + "mac": "2.2081", + "android": "2.2081", + "ios": "2.2081" }, "id": "gdutilsdevs.gdutils", "name": "GDUtils", @@ -16,8 +16,7 @@ "source": "https://github.com/Jouca/GDUtils-Geode" }, "dependencies": { - "geode.node-ids": ">=v1.21.1", - "jouca.badgesapi": ">=v1.2.4" + "geode.node-ids": ">=v1.21.1" }, "api": { "include": [ "include/*.hpp" ] @@ -31,7 +30,8 @@ ], "resources": { "files": [ - "resources/gauntlets/*.png" + "resources/gauntlets/*.png", + "resources/gauntlets/particles.json" ], "sprites": [ "resources/*.png" @@ -267,11 +267,18 @@ "description": "This category is for any other feature for GDUtils.\nYou can choose whether to enable or disable some options if you do not want them.", "type": "title" }, + "clickableBadges": { + "name": "Clickable Badges", + "description": "Makes badges in Profile's clickable, showing more information about them.", + "type": "bool", + "default": true + }, "customBadges": { "name": "Custom Badges", "description": "Whether or not to show custom badges in Profile and Comments.", "type": "bool", - "default": true + "default": true, + "enable-if": "clickableBadges" }, "friendSearch": { "name": "Search Friends", diff --git a/src/modules/events/EventHandler.cpp b/src/modules/events/EventHandler.cpp deleted file mode 100644 index bdf609c..0000000 --- a/src/modules/events/EventHandler.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include "EventHandler.hpp" -#include "EventsPush.hpp" -#include - -//TODO: -void processChestEvent(CCScene* self) { - /*if (!chestQueue.empty()) { - auto data = chestQueue.front(); - chestQueue.pop(); - auto dailyChest = new DailyChest(); - dailyChest->getRewards(0); - }*/ -} diff --git a/src/modules/events/EventHandler.hpp b/src/modules/events/EventHandler.hpp deleted file mode 100644 index f79cb08..0000000 --- a/src/modules/events/EventHandler.hpp +++ /dev/null @@ -1,118 +0,0 @@ -#pragma once -#include -#include - -class EventHandler : public CCObject { - protected: - std::string m_currentLayer; - public: - static EventHandler* create() { - auto pRet = new EventHandler(); - if (pRet) { - pRet->autorelease(); - return pRet; - } - CC_SAFE_DELETE(pRet); - return nullptr; - } - void checkForEvent(); - static void aaa(); -#if 0 - void checkForFiles() { - #ifdef GEODE_IS_WINDOWS - auto scene = CCDirector::sharedDirector()->getRunningScene(); - if (!scene) return; - if (scene->getChildrenCount() == 0) return; - auto layer = scene->getChildren()->objectAtIndex(0); - if (layer == nullptr) return; - std::string layerName = misc::getNodeName(layer); - if (layerName == "cocos2d::CCLayerColor") return; - if (layerName == "LoadingLayer") return; - if (layerName == "LevelEditorLayer") return; - if (layerName == "PlayLayer") return; - if (layerName == "LoadingLayer") return; - bool levelsProtocol = Mod::get()->template getSettingValue("levelsProtocol"); - if (!levelsProtocol) return; - auto glm = GameLevelManager::sharedState(); - try { - for (const auto & entry : std::filesystem::directory_iterator("gdutils")) { - if (entry.path().extension() == ".flag") { - std::string path = entry.path().string(); - std::string filename = entry.path().filename().string(); - std::string levelName = filename.substr(0, filename.length() - 5); - - try { - std::filesystem::remove("gdutils/" + filename); - } catch (const std::exception& e) { - break; - } - - std::string url = "https://www.boomlings.com/database/getGJLevels21.php"; - std::string fields = fmt::format("secret=Wmfd2893gb7&type=0&str={}", levelName); - - geode::utils::web::WebRequest request = web::WebRequest(); - request.userAgent(""); - request.header("Content-Type", "application/x-www-form-urlencoded"); - const std::lock_guard lock(lock_var); - RUNNING_REQUESTS.emplace( - "@loaderLevelProtocolURL", - request.bodyString(fields).post(url).map( - [](web::WebResponse* response) { - const std::lock_guard lock(lock_var); - if (response->ok()) { - if (response->data().empty()) { - FLAlertLayer::create(nullptr, - "Error (GDUtils)", - "An error has occured with Robtop's servers. The response returned empty.", - "OK", - nullptr, - 180.0F - )->show(); - } else { - auto data = response->string().unwrapOrDefault(); - if (data != "-1") { - auto scene = CCScene::create(); - - GJGameLevel* gjgl = EventsPush::convertLevelToJSON(data); - - auto layer = LevelInfoLayer::create(gjgl, false); - layer->downloadLevel(); - scene->addChild(layer); - CCDirector::sharedDirector()->pushScene(cocos2d::CCTransitionFade::create(0.5f, scene)); - } else { - FLAlertLayer::create(nullptr, - "Error", - "Level not found.", - "OK", - nullptr, - 180.0F - )->show(); - } - } - } else { - FLAlertLayer::create(nullptr, - "Error (GDUtils)", - "An error has occured with Robtop's servers. The reason could be because you are either ratelimited, blocked, or another issue.", - "OK", - nullptr, - 180.0F - )->show(); - } - - RUNNING_REQUESTS.erase("@loaderLevelProtocolURL"); - return *response; - } - ) - ); - } - } - } catch (const std::exception& e) {} - #endif - } -#endif - void check(float dt) { - checkForEvent(); - //checkForFiles(); - } -}; - diff --git a/src/ui/OldBorder.cpp b/src/ui/OldBorder.cpp new file mode 100644 index 0000000..5b4060b --- /dev/null +++ b/src/ui/OldBorder.cpp @@ -0,0 +1,119 @@ +#include "OldBorder.hpp" + +OldBorder* OldBorder::create(CCNode* node, const ccColor4B& backgroundColor, const CCSize& size, const CCPoint& padding) { + OldBorder* instance = new OldBorder(padding); + + if (instance && instance->init(node, backgroundColor, size)) { + instance->autorelease(); + return instance; + } else { + delete instance; + return nullptr; + } +} + +OldBorder::OldBorder(const CCPoint& padding) : m_padding({ padding.x, padding.y, padding.x, padding.y }) { } + +bool OldBorder::init(const ccColor4B& backgroundColor, const CCSize& size) { + return this->init(nullptr, backgroundColor, size); +} + +bool OldBorder::init(CCNode* node, const ccColor4B& backgroundColor, const CCSize& size) { + if (!this->initWithColor(backgroundColor)) { + return false; + } + Build::create("inverseborder.png"_spr) + .id("border_sprite"_spr) + .anchorPoint({0,0}) + .pos(0,0) + .zOrder(1) + .parent(this) + .store(m_inverseBorder); + Build::create().id("border_content"_spr).contentSize(size).parent(this).store(m_content); + if (node != nullptr) { + this->setNode(node); + } + this->setSize(size); + return true; +} + + +void OldBorder::setPadding(float x, float y) { + this->setPaddingX(x); + this->setPaddingY(y); +} + +void OldBorder::setPaddingX(float x) { + this->setPaddingLeft(x); + this->setPaddingRight(x); +} + +float OldBorder::getPaddingX() { + return (m_padding.left + m_padding.right) / 2; +} + +void OldBorder::setPaddingY(float y) { + this->setPaddingTop(y); + this->setPaddingBottom(y); +} + +float OldBorder::getPaddingY() { + return (m_padding.top + m_padding.bottom) / 2; +} + +void OldBorder::setPaddingTop(float top) { + m_padding.top = top; + + this->updatePadding(); +} + + +void OldBorder::setPaddingRight(float right) { + m_padding.right = right; + + this->updatePadding(); +} + +void OldBorder::setPaddingBottom(float bottom) { + m_padding.bottom = bottom; + + this->updatePadding(); +} + +void OldBorder::setPaddingLeft(float left) { + m_padding.left = left; + + this->updatePadding(); +} +void OldBorder::setBackgroundColor(const ccColor4B& color) { + this->setColor({ color.r, color.g, color.b }); + this->setOpacity(color.a); +} + +ccColor4B OldBorder::getBackgroundColor() { + return { _displayedColor.r, _displayedColor.g, _displayedColor.b, _displayedOpacity }; +} + +void OldBorder::setNode(CCNode* node) { + // Can't assume an ID as the node is a user input and may have its ID changed + if (CCNode* oldNode = m_content->getChildByType(0)) { + // Not going to mess with releasing the node, I'll leave that to the user + oldNode->removeFromParent(); + } + m_content->addChild(node); + this->updatePadding(); +} + +void OldBorder::setSize(const CCSize& size) { + this->setContentSize(size); + m_inverseBorder->setContentSize(size); + m_content->setContentSize(size); + this->updatePadding(); +} + +void OldBorder::updatePadding() { + CCSize size = this->getContentSize(); + m_content->setAnchorPoint({ 0, 0 }); + m_content->setPosition({ m_padding.left, m_padding.bottom }); + m_content->setContentSize(size - ccp(this->getPaddingX(), this->getPaddingY()) * 2); +} diff --git a/src/ui/OldBorder.hpp b/src/ui/OldBorder.hpp new file mode 100644 index 0000000..d1dd8e3 --- /dev/null +++ b/src/ui/OldBorder.hpp @@ -0,0 +1,44 @@ +#pragma once +#include + +class OldBorder : public cocos2d::CCLayerColor { +public: + struct Padding { + float top; + float right; + float bottom; + float left; + }; + + static OldBorder* create(cocos2d::CCNode* node, const cocos2d::ccColor4B& backgroundColor, const cocos2d::CCSize& size = { 0, 0 }, const cocos2d::CCPoint& padding = { 0, 0 }); + + void setPadding(const cocos2d::CCPoint& padding) { this->setPadding(padding.x, padding.y); } + void setPadding(float x, float y); + void setPadding(float padding) { this->setPadding(padding, padding); } + Padding getPadding() { return m_padding; } + void setPaddingX(float x); + float getPaddingX(); + void setPaddingY(float y); + float getPaddingY(); + void setPaddingTop(float top); + float getPaddingTop() { return m_padding.top; } + void setPaddingRight(float right); + float getPaddingRight() { return m_padding.right; } + void setPaddingBottom(float bottom); + float getPaddingBottom() { return m_padding.bottom; } + void setPaddingLeft(float left); + float getPaddingLeft() { return m_padding.left; } + void setBackgroundColor(const cocos2d::ccColor4B& color); + cocos2d::ccColor4B getBackgroundColor(); + void setNode(CCNode* node); + void setSize(const cocos2d::CCSize& size); +protected: + Padding m_padding; + CCLayer* m_content; + CCScale9Sprite* m_inverseBorder; + OldBorder(const cocos2d::CCPoint& padding = { 0, 0 }); + bool init(const cocos2d::ccColor4B& backgroundColor, const cocos2d::CCSize& size); + bool init(CCNode* node, const cocos2d::ccColor4B& backgroundColor, const cocos2d::CCSize& size); +private: + void updatePadding(); +}; From 61794200f99ef0f0eb2967d90e1da3c5b8d7a0a7 Mon Sep 17 00:00:00 2001 From: FireMario211 <17692105+FireMario211@users.noreply.github.com> Date: Sat, 21 Feb 2026 17:28:15 -0500 Subject: [PATCH 05/32] whoops --- mod.json | 2 +- src/main.cpp | 11 +++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/mod.json b/mod.json index 9e35d04..e8f6827 100644 --- a/mod.json +++ b/mod.json @@ -1,5 +1,5 @@ { - "geode": "5.0.0-beta.2", + "geode": "5.0.0-beta.3", "version": "v1.5.4", "gd": { "win": "2.2081", diff --git a/src/main.cpp b/src/main.cpp index cc09cd7..94287a5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2,6 +2,7 @@ // Ported with help by Firee // Mod made by Jouca & Firee +//#include "defs/FileWatcher.hpp" #include "modules/events/DailyChest.hpp" $execute { // listenForSettingChanges("activate-background", [](bool value) { @@ -16,10 +17,10 @@ #include #include #include -#include #include -bool g_socket_server_ran = false; +static bool g_socket_server_ran = false; +//static FileWatcher* g_watcher; $execute { if (Mod::get()->getSettingValue("server-notification")) { @@ -73,6 +74,12 @@ bool g_socket_server_ran = false; ); } }); + /*auto watcher = FileWatcher::getForDirectory("gdutils"); + watcher->watch("gdutils.heartbeat", [] { + }); + async::runtime().spawn([]() -> arc::Future<> { + co_return; + });*/ } $on_game(Loaded) { From 260b57b3c9ddc958e6793e44747084bb92b43f88 Mon Sep 17 00:00:00 2001 From: FireMario211 <17692105+FireMario211@users.noreply.github.com> Date: Sat, 7 Mar 2026 14:39:38 -0500 Subject: [PATCH 06/32] events ui temp fix --- include/RateEvent.hpp | 44 ---- include/Types.hpp | 47 +++- mod.json | 5 +- src/modules/events/DailyChest.cpp | 2 +- src/modules/events/EventsPush.cpp | 417 +++++++++++++++++++++++++++++- src/modules/events/EventsPush.hpp | 4 +- src/network/AMQClient.cpp | 1 + 7 files changed, 466 insertions(+), 54 deletions(-) diff --git a/include/RateEvent.hpp b/include/RateEvent.hpp index 6999984..20446ec 100644 --- a/include/RateEvent.hpp +++ b/include/RateEvent.hpp @@ -10,50 +10,6 @@ namespace GDUtils::Events { static void emit(EventData data) { RateEvent().send(data); }; - static EventType intToType(int type) { - switch (type) { - default: - return EventType::NA; - case 0: // Rate - return EventType::Rate; - case 1: // Daily - return EventType::Daily; - case 2: // Weekly - return EventType::Weekly; - case 3: // Small chest - return EventType::smallChest; - case 4: // Large chest - return EventType::largeChest; - case 5: // List - return EventType::List; - case 6: // Event - return EventType::Event; - case 7: // Announcement - return EventType::Announcement; - } - } - static int typeToInt(EventType type) { - switch (type) { - case EventType::NA: - return -1; - case EventType::Rate: // Rate - return 0; - case EventType::Daily: // Daily - return 1; - case EventType::Weekly: // Weekly - return 2; - case EventType::smallChest: // Small chest - return 3; - case EventType::largeChest: // Large chest - return 4; - case EventType::List: // List - return 5; - case EventType::Event: // Event - return 6; - case EventType::Announcement: // Announcement - return 7; - } - } }; struct OnRate final : geode::GlobalEvent { using GlobalEvent::GlobalEvent; diff --git a/include/Types.hpp b/include/Types.hpp index 3f686a8..f8213ca 100644 --- a/include/Types.hpp +++ b/include/Types.hpp @@ -1,5 +1,6 @@ #pragma once #include +#include "RateEvent.hpp" enum EventType { Rate, Daily, @@ -32,6 +33,50 @@ struct EventData { EventType getEventType() const { return type; }; std::string getLevelName() const { return level_name; }; int getLevelID() const { return level_id; }; + int typeToInt() const { + switch (type) { + case EventType::NA: + return -1; + case EventType::Rate: // Rate + return 0; + case EventType::Daily: // Daily + return 1; + case EventType::Weekly: // Weekly + return 2; + case EventType::smallChest: // Small chest + return 3; + case EventType::largeChest: // Large chest + return 4; + case EventType::List: // List + return 5; + case EventType::Event: // Event + return 6; + case EventType::Announcement: // Announcement + return 7; + } + } + EventType intToType(int intType) const { + switch (intType) { + default: + return EventType::NA; + case 0: // Rate + return EventType::Rate; + case 1: // Daily + return EventType::Daily; + case 2: // Weekly + return EventType::Weekly; + case 3: // Small chest + return EventType::smallChest; + case 4: // Large chest + return EventType::largeChest; + case 5: // List + return EventType::List; + case 6: // Event + return EventType::Event; + case 7: // Announcement + return EventType::Announcement; + } + } }; template<> @@ -121,7 +166,7 @@ struct matjson::Serialize { val.set("starsum", value.starsum); val.set("stars", value.stars); val.set("rate", value.rate); - val.set("type", (int)value.type); + val.set("type", value.typeToInt()); val.set("title", value.title); val.set("level_name", value.level_name); val.set("level_creator", value.level_creator); diff --git a/mod.json b/mod.json index e8f6827..967f70b 100644 --- a/mod.json +++ b/mod.json @@ -1,5 +1,5 @@ { - "geode": "5.0.0-beta.3", + "geode": "5.0.0", "version": "v1.5.4", "gd": { "win": "2.2081", @@ -16,7 +16,8 @@ "source": "https://github.com/Jouca/GDUtils-Geode" }, "dependencies": { - "geode.node-ids": ">=v1.21.1" + "geode.node-ids": ">=v1.21.1", + "jouca.badgesapi": ">=v1.4.1" }, "api": { "include": [ "include/*.hpp" ] diff --git a/src/modules/events/DailyChest.cpp b/src/modules/events/DailyChest.cpp index 3a93930..8f70412 100644 --- a/src/modules/events/DailyChest.cpp +++ b/src/modules/events/DailyChest.cpp @@ -240,7 +240,7 @@ void giveChestNotify(bool large) { }); listenForSettingChanges("largeChest", [](bool value) { if (!value && !Mod::get()->getSettingValue("smallChest")) { - if (g_chestTx) { + if (g_chestTx) { (void)g_chestTx->trySend({ ChestMsg::Type::Stop }); } } diff --git a/src/modules/events/EventsPush.cpp b/src/modules/events/EventsPush.cpp index dc81ebd..95ded17 100644 --- a/src/modules/events/EventsPush.cpp +++ b/src/modules/events/EventsPush.cpp @@ -87,6 +87,415 @@ bool EventsPush::init(EventData data) { // 1 = daily // 2 = weekly + // rate + // 0 = unrated / star rate + // 1 = featured + // 2 = epic + // {'demon': 0, 'type': 0, 'starsum': 20, 'stars': 2, 'rate': 2, 'title': 'New Rated Level !', 'level_name': 'Pourquoi', 'level_creator': 'by Jouca', 'sprite': 'GJ_square01.png'} + int starsum = data.starsum; + int stars = data.stars; + int rateType = data.rate; + int coins = data.coins; + std::unordered_map sKeys { + { EventType::Rate, "newRate" }, + { EventType::Daily, "daily" }, + { EventType::Weekly, "weekly" }, + { EventType::smallChest, "event" }, + { EventType::largeChest, "smallChest" }, + { EventType::List, "largeChest" }, + { EventType::Event, "newListRate" }, + { EventType::Announcement, "announcement" }, + }; + eventType = data.type; + auto it = sKeys.find(data.type); + if (it != sKeys.end()) { + if (!Mod::get()->getSettingValue(it->second)) { + EventsPush::eventCompletedCallback(scene); + return true; + } + } + float lrScale = (float)Mod::get()->template getSettingValue("size"); + auto bg = Build::create(data.sprite.c_str()).scale(lrScale).contentSize(240, 70).collect(); + // 0 / 0 = bottom left + // screen width / screen height = top right + // screen width divided by 2 / screen height divided by 2 = middle + this->addChild(bg); + auto node = CCNode::create(); + + if (eventType != EventType::smallChest && eventType != EventType::largeChest && eventType != EventType::Announcement) { + CCSprite* diffFace; + GJDifficultySprite* mythic = nullptr; + if (!data.demon) { + diffFace = cocos2d::CCSprite::createWithSpriteFrameName(getDifficultyIcon(starsum)); + mythic = GJDifficultySprite::create(static_cast(getDifficulty(starsum)), static_cast(0)); + } else { + diffFace = cocos2d::CCSprite::createWithSpriteFrameName(getDemonDifficultyIcon(starsum)); + mythic = GJDifficultySprite::create(static_cast(getDemonDifficulty(starsum)), static_cast(0)); + } + + diffFace->setPosition({26.f, 43.f}); + diffFace->setScale(.8F); + CCSprite* moon = cocos2d::CCSprite::createWithSpriteFrameName("moon_small01_001.png"); + mythic->updateFeatureState(static_cast(4)); + + std::string starcountstr = std::to_string(stars); + auto starcount = cocos2d::CCLabelBMFont::create(starcountstr.c_str(), "bigFont.fnt"); + starcount->setPosition({25, 18}); + starcount->setAnchorPoint({1, 0.5}); + starcount->setScale(0.35f); + bg->addChild(starcount); + CCSprite* star; + if (eventType == EventType::List) { + star = cocos2d::CCSprite::createWithSpriteFrameName("diamond_small01_001.png"); + } else { + star = cocos2d::CCSprite::createWithSpriteFrameName("star_small01_001.png"); + } + star->setPosition({31, 18}); + star->setScale(.775F); + moon->setPosition({31, 18}); + moon->setScale(.775F); + + if (data.platformer) bg->addChild(moon); + else bg->addChild(star); + + mythic->setPosition({26.f, 43.f}); + mythic->setScale(.8F); + + switch (rateType) { + default: // Rate + bg->addChild(diffFace); + break; + case 1: // Featured + Build::createSpriteName("GJ_featuredCoin_001.png").pos(26,43).scale(.8f).parent(bg); + bg->addChild(diffFace); + break; + case 2: // Epic + Build::createSpriteName("GJ_epicCoin_001.png").pos(26,43).scale(.8f).parent(bg); + bg->addChild(diffFace); + break; + case 3: // Legendary + Build::createSpriteName("GJ_epicCoin2_001.png").pos(26,43).scale(.8f).parent(bg); + bg->addChild(diffFace); + if (Mod::get()->template getSettingValue("customDifficultyFaces")) { + CCSprite* legendaryFace = nullptr; + if (!data.demon) { + if (starsum >= 10) { + std::string diffStr = std::to_string(static_cast(getDifficulty(starsum))); + auto name = "difficulty_0" + diffStr + "_legendaryIcon.png"; + legendaryFace = CCSprite::create((""_spr+name).c_str()); + } + } else { + if (starsum < 6) { + std::string diffStr = std::to_string(static_cast(getDemonDifficulty(starsum))); + auto name = "difficulty_0" + diffStr + "_legendaryIcon.png"; + legendaryFace = CCSprite::create((""_spr+name).c_str()); + } else { + std::string diffStr = std::to_string(static_cast(getDemonDifficulty(starsum))); + auto name = "difficulty_" + diffStr + "_legendaryIcon.png"; + legendaryFace = CCSprite::create((""_spr+name).c_str()); + } + } + + if (legendaryFace != nullptr) { + legendaryFace->setPosition({26.f, 48.f}); + legendaryFace->setScale(.8F); + bg->addChild(legendaryFace); + } + } + break; + case 4: // Mythic + bg->addChild(mythic); + if (Mod::get()->template getSettingValue("customDifficultyFaces")) { + CCSprite* mythicFace = nullptr; + if (!data.demon) { + if (starsum >= 10) { + std::string diffStr = std::to_string(static_cast(getDifficulty(starsum))); + auto name = "difficulty_0" + diffStr + "_mythicIcon.png"; + mythicFace = CCSprite::create((""_spr+name).c_str()); + } + } else { + if (starsum < 6) { + std::string diffStr = std::to_string(static_cast(getDemonDifficulty(starsum))); + auto name = "difficulty_0" + diffStr + "_mythicIcon.png"; + mythicFace = CCSprite::create((""_spr+name).c_str()); + } else { + std::string diffStr = std::to_string(static_cast(getDemonDifficulty(starsum))); + auto name = "difficulty_" + diffStr + "_mythicIcon.png"; + mythicFace = CCSprite::create((""_spr+name).c_str()); + } + } + + if (mythicFace != nullptr) { + mythicFace->setPosition({26.f, 48.f}); + mythicFace->setScale(.8F); + bg->addChild(mythicFace); + } + } + + break; + } + + auto verifiedCoinSpr1 = cocos2d::CCSprite::createWithSpriteFrameName("GJ_coinsIcon2_001.png"); + verifiedCoinSpr1->setScale(0.35f); + auto verifiedCoinSpr2 = cocos2d::CCSprite::createWithSpriteFrameName("GJ_coinsIcon2_001.png"); + verifiedCoinSpr2->setScale(0.35f); + auto verifiedCoinSpr3 = cocos2d::CCSprite::createWithSpriteFrameName("GJ_coinsIcon2_001.png"); + verifiedCoinSpr3->setScale(0.35f); + + if (data.verified_coins == 0) { + verifiedCoinSpr1->setColor({255, 175, 75}); + verifiedCoinSpr2->setColor({255, 175, 75}); + verifiedCoinSpr3->setColor({255, 175, 75}); + } + + if (coins == 1 || coins == 3) { + verifiedCoinSpr2->setPosition({ -52, -22 }); + node->addChild(verifiedCoinSpr2); + + if (coins == 3) { + verifiedCoinSpr1->setPosition({ -60, -22 }); + verifiedCoinSpr3->setPosition({ -44, -22 }); + + node->addChild(verifiedCoinSpr1); + node->addChild(verifiedCoinSpr3); + } + } else if (coins == 2) { + verifiedCoinSpr1->setPosition({ -55, -22 }); + verifiedCoinSpr2->setPosition({ -48, -22 }); + + node->addChild(verifiedCoinSpr1); + node->addChild(verifiedCoinSpr2); + } + + // Progress Bar for lists + if (eventType == EventType::List) { + auto barSpriteBack = CCSprite::create("GJ_progressBar_001.png"); + barSpriteBack->setScaleX(0.38f); + barSpriteBack->setScaleY(0.45f); + barSpriteBack->setPosition({ 38, 3 }); + barSpriteBack->setPositionY(3 - 19.f); + barSpriteBack->setPositionX(38); + barSpriteBack->setColor({0, 0, 0}); + barSpriteBack->setOpacity(100); + barSpriteBack->setZOrder(10); + barSpriteBack->setID("progress-bar-list"_spr); + node->addChild(barSpriteBack); + + auto barSpriteTop = CCSprite::create("GJ_progressBar_001.png"); + barSpriteTop->setScaleY(0.4f); + barSpriteTop->setPosition({ 2, 10 }); + barSpriteTop->setAnchorPoint({0, 0.5}); + barSpriteTop->setOpacity(255); + barSpriteTop->setZOrder(11); + + CCPoint rectangle[4] = { + CCPoint(0, 0), + CCPoint(0, 20), + CCPoint(barSpriteBack->getScaledContentSize().width * -2.5f, 20), + CCPoint(barSpriteBack->getScaledContentSize().width * -2.5f, 0) + }; + + auto clippingNode = CCClippingNode::create(); + auto barMask = CCDrawNode::create(); + barMask->drawPolygon(rectangle, 4, ccc4FFromccc3B({0, 0, 0}), 0, ccc4FFromccc3B({0, 0, 0})); + clippingNode->setStencil(barMask); + clippingNode->addChild(barSpriteTop); + clippingNode->setPositionX(330); + barSpriteBack->addChild(clippingNode); + barSpriteTop->setPositionX(-666); + + // Get datas for levels + int maxToCompleteList = data.maxToCompleteList; + std::vector levels = geode::utils::string::split(data.levels_list, ","); + + // Get client data for levels + GameLevelManager* gameLevelManager = GameLevelManager::sharedState(); + CCArray* levelsData = gameLevelManager->getCompletedLevels(false); + + int completedLevels = 0; + int nbLevels = 0; + + while (levels.size() > 0) { + std::string id = levels[0]; + nbLevels++; + + for (int i = 0; i < levelsData->count(); i++) { + auto level = static_cast(levelsData->objectAtIndex(i)); + if (std::to_string(level->m_levelID) == id) { + if (level->m_normalPercent == 100) completedLevels++; + } + } + + levels.erase(levels.begin()); + } + + // min -666 | max -337 + // Calculate position for Progress Bar (between these values based on percentage) + float percentage; + CCLabelBMFont* progressText; + + if (completedLevels >= maxToCompleteList) { + percentage = (float)completedLevels / (float)nbLevels; + progressText = CCLabelBMFont::create((std::to_string(completedLevels) + "/" + std::to_string(nbLevels)).c_str(), "bigFont.fnt"); + barSpriteTop->setColor({ 100, 255, 255 }); + starcount->setColor({ 100, 255, 255 }); + + auto listCompleted = CCSprite::createWithSpriteFrameName("GJ_completesIcon_001.png"); + listCompleted->setPosition({ 94, -15 }); + listCompleted->setScale(0.55f); + listCompleted->setZOrder(13); + node->addChild(listCompleted); + } else { + percentage = (float)completedLevels / (float)maxToCompleteList; + progressText = CCLabelBMFont::create((std::to_string(completedLevels) + "/" + std::to_string(maxToCompleteList)).c_str(), "bigFont.fnt"); + barSpriteTop->setColor({ 255, 84, 50 }); + + auto listUncompleted = CCSprite::createWithSpriteFrameName("diamond_small01_001.png"); + listUncompleted->setPosition({ 94, -15 }); + listUncompleted->setScale(0.55f); + listUncompleted->setZOrder(13); + node->addChild(listUncompleted); + } + + auto moveAction = CCEaseSineOut::create(CCMoveBy::create(0.f, { (percentage * 337), 0 })); + barSpriteTop->runAction(moveAction); + + // Add text for Progress Bar + progressText->setPosition({ 38, -15 }); + progressText->setScale(0.35f); + progressText->setZOrder(13); + node->addChild(progressText); + } + } + + std::string titleStr = data.title; + CCPoint titlePos; + geode::Anchor titleAnchor = Anchor::Top; + switch (data.type) { + case EventType::smallChest: + titlePos = CCPoint(0, -14); + Build::createSpriteName("chest_01_02_001.png").scale(0.45f).parentAtPos(bg, Anchor::Center, {0,-8}); + break; + case EventType::largeChest: + titlePos = CCPoint(0, -14); + Build::createSpriteName("chest_02_02_001.png").scale(0.4f).parentAtPos(bg, Anchor::Center, {0,-8}); + break; + case EventType::List: // TEST + titlePos = CCPoint(-11, -15); + Build::createSpriteName("GJ_listAddBtn_001.png").anchorPoint(1,0).parentAtPos(bg, Anchor::BottomLeft, {7,7}); + break; + case EventType::Announcement: // TEST + titleStr = "Announcement"; + titlePos = CCPoint(-56, -14); + Build::create(data.title.c_str(), "bigFont.fnt").limitLabelWidth(200, 0.46f, 0.1f).anchorPoint(0, 0.5).parentAtPos(bg, Anchor::Left, {12,5}); + break; + default: + titlePos = CCPoint(-6, -17); + break; + } + Build::create(titleStr.c_str(), "goldFont.fnt").scale(0.575F).parentAtPos(bg, titleAnchor, titlePos); + + if (eventType != EventType::Announcement) { + auto level_title = cocos2d::CCLabelBMFont::create(data.level_name.c_str(), "bigFont.fnt"); + if (eventType == EventType::List) { + level_title->setPosition({ -27, 11 }); + } else { + level_title->setPosition({ -27, 3 }); + } + level_title->setScale(.46F); + + level_title->setAnchorPoint({ 0, 0.5 }); + + auto level_by = cocos2d::CCLabelBMFont::create(data.level_creator.c_str(), "goldFont.fnt"); + if (eventType == EventType::List) { + level_by->setPosition({ -27, -2 }); + } else { + level_by->setPosition({ -27, -11 }); + } + level_by->setScale(.46F); + level_by->limitLabelWidth(120, 0.46f, 0.1f); + level_by->setAnchorPoint({ 0, 0.5 }); + node->addChild(level_by); + level_title->limitLabelWidth(120, 0.46f, 0.1f); + node->addChild(level_title); + } + + if (eventType == EventType::Daily || eventType == EventType::Weekly || eventType == EventType::Event) { + CCSprite* crown; + if (eventType == EventType::Daily) { + crown = cocos2d::CCSprite::createWithSpriteFrameName("gj_dailyCrown_001.png"); + } else if (eventType == EventType::Event) { + crown = cocos2d::CCSprite::createWithSpriteFrameName("gj_eventCrown_001.png"); + } else { + crown = cocos2d::CCSprite::createWithSpriteFrameName("gj_weeklyCrown_001.png"); + } + crown->setScale(.45F); + crown->setPosition({ 45, 45 }); + node->addChild(crown); + } + + node->setAnchorPoint({ 0.5, 0.5 }); + node->setPosition({ 77, 30 }); + + bg->addChild(node); + + // Move action + + float delayTime = (float)Mod::get()->template getSettingValue("time"); + + float moveX = .0F; + switch (Mod::get()->getSettingValue("notificationPlacement")) { + case PositionEnum::TopLeft: // top left + bg->setPosition((-(bg->getContentSize().width / 2)) * lrScale, ((winSize.height - (bg->getContentSize().height / 2)) - (20 * lrScale))); + moveX = (bg->getContentSize().width) * lrScale; + break; + case PositionEnum::TopRight: // top right + bg->setPosition(winSize.width + ((bg->getContentSize().width / 2) * lrScale), ((winSize.height - (bg->getContentSize().height / 2)) - (20 * lrScale))); + moveX = -(bg->getContentSize().width * lrScale); + break; + case PositionEnum::BottomLeft: // bottom left + bg->setPosition((-(bg->getContentSize().width / 2)) * lrScale, (bg->getContentSize().height / 2) * lrScale); + moveX = (bg->getContentSize().width) * lrScale; + break; + case PositionEnum::BottomRight: // bottom right + bg->setPosition({ winSize.width + ((bg->getContentSize().width / 2) * lrScale), (bg->getContentSize().height / 2) * lrScale}); + moveX = -((bg->getContentSize().width) * lrScale); + break; + } + + if (Mod::get()->getSettingValue("sfx")) { + float volume = (Mod::get()->getSettingValue("sfx-link")) ? GameManager::get()->m_sfxVolume : Mod::get()->getSettingValue("sfx-volume"); + if (data.type != EventType::smallChest && data.type != EventType::largeChest) { + FMODAudioEngine::get()->playEffect("crystal01.ogg", 1, 1, volume); + } else { + FMODAudioEngine::get()->playEffect("reward01.ogg", 1, 1, volume); + } + } + + bg->runAction(CCSequence::create( + CCEaseOut::create(CCMoveBy::create(0.5f, { moveX, 0.0f }), 0.6f), + CCDelayTime::create(delayTime), + CCEaseIn::create(CCMoveBy::create(0.5f, { -moveX, 0.0f }), 0.6f), + CCDelayTime::create(0.5F), + CCCallFunc::create(this, callfunc_selector(EventsPush::destroySelf)), + nullptr + )); + + this->setZOrder(scene->getHighestChildZ() + 10); + this->setID("EventsPush"_spr); + return true; +} + +bool EventsPush::initold(EventData data) { + if (!CCLayer::init()) return false; + auto scene = CCDirector::sharedDirector()->getRunningScene(); + auto director = CCDirector::sharedDirector(); + auto winSize = director->getWinSize(); + // type + // 0 = new rate + // 1 = daily + // 2 = weekly + // rate // 0 = unrated / star rate // 1 = featured @@ -321,9 +730,9 @@ bool EventsPush::init(EventData data) { bg->runAction(CCSequence::create( CCEaseOut::create(CCMoveBy::create(0.5f, { moveX, 0.0f }), 0.6f), CCDelayTime::create(Mod::get()->getSettingValue("time")), - CCEaseIn::create(CCMoveBy::create(0.5f, { -moveX, 0.0f }), 0.6f), - CCDelayTime::create(0.5F), - CCCallFunc::create(this, callfunc_selector(EventsPush::destroySelf)), + //CCEaseIn::create(CCMoveBy::create(0.5f, { -moveX, 0.0f }), 0.6f), + //CCDelayTime::create(0.5F), + //CCCallFunc::create(this, callfunc_selector(EventsPush::destroySelf)), nullptr )); @@ -352,7 +761,7 @@ void EventsPush::pushRateLevel(CCScene *self, EventData data) { data.starsum, data.stars, data.rate, - GDUtils::Events::RateEvent::typeToInt(data.type), + data.typeToInt(), data.title, data.sprite, data.level_name, diff --git a/src/modules/events/EventsPush.hpp b/src/modules/events/EventsPush.hpp index 612c585..672c921 100644 --- a/src/modules/events/EventsPush.hpp +++ b/src/modules/events/EventsPush.hpp @@ -6,9 +6,9 @@ class EventsPush : public CCLayer { protected: bool m_alreadyDead = false; - GJGameLevel* level = GJGameLevel::create(); EventType eventType = EventType::NA; - virtual bool init(EventData data); + bool init(EventData data); + bool initold(EventData data); void destroySelf() { m_alreadyDead = true; auto scene = CCDirector::get()->getRunningScene(); diff --git a/src/network/AMQClient.cpp b/src/network/AMQClient.cpp index 5ae23ba..0e23e13 100644 --- a/src/network/AMQClient.cpp +++ b/src/network/AMQClient.cpp @@ -60,6 +60,7 @@ std::string AMQErrorToString(amqp_rpc_reply_t reply) { errorStr = "Unknown Error"; } break; + case AMQP_RESPONSE_NORMAL: break; } return fmt::format("{} ({})", errorStr, AMQReplyToString(reply.reply_type)); } From 0e11a19afa46cd3a0fd6f2cbca84ca57ee30a078 Mon Sep 17 00:00:00 2001 From: FireMario211 <17692105+FireMario211@users.noreply.github.com> Date: Sat, 7 Mar 2026 19:14:46 -0500 Subject: [PATCH 07/32] push version --- changelog.md | 6 +++++- mod.json | 2 +- src/hooks/LeaderboardsLayer.cpp | 19 +++++++++++++++++++ test/src/main.cpp | 2 +- 4 files changed, 26 insertions(+), 3 deletions(-) create mode 100644 src/hooks/LeaderboardsLayer.cpp diff --git a/changelog.md b/changelog.md index 7164f95..aae1b33 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,10 @@ # Changelog -## v... (---) +## v1.6.0-beta.1 (2026-03-08) +* GDUtils ported to Geode 5.0.0 (GD 2.2081) +* Changed logic for chest notifications +* Temporarily removed leaderboards due to not being rewritten yet. * Fixed Spotify feature not unmuting when playtesting in Editor. +* Fixed notification being clickable ## v1.5.4 (2025-09-09) * Added coloured container animation when searching a friend on the friend list. diff --git a/mod.json b/mod.json index 967f70b..933c35c 100644 --- a/mod.json +++ b/mod.json @@ -1,6 +1,6 @@ { "geode": "5.0.0", - "version": "v1.5.4", + "version": "v1.6.0-beta.1", "gd": { "win": "2.2081", "mac": "2.2081", diff --git a/src/hooks/LeaderboardsLayer.cpp b/src/hooks/LeaderboardsLayer.cpp new file mode 100644 index 0000000..a6fc123 --- /dev/null +++ b/src/hooks/LeaderboardsLayer.cpp @@ -0,0 +1,19 @@ +#include +#include +//#include + +class $modify(LeaderboardsLayer) { + bool init(LeaderboardType type, LeaderboardStat stat) { + if (!LeaderboardsLayer::init(type, stat)) return false; + Build::create().with([](auto node) { + Build::createSpriteName("GJ_plusBtn_001.png").scale(.8f).intoMenuItem([]() { + //CCDirector::sharedDirector()->pushScene(CCTransitionFade::create(0.5, MoreLeaderboards::scene())); + FLAlertLayer::create("Notice", "This currently has not been implemented yet!\nPlease wait until the next major update for this feature to be added.", "OK")->show(); + }).pos(-239,20).id("more-leaderboards-btn"_spr).parent(node); + Build::createSpriteName("modBadge_01_001.png").scale(1.5f).intoMenuItem([]() { + FLAlertLayer::create("Notice", "This currently has not been implemented yet!\nPlease wait until the next major update for this feature to be added.", "OK")->show(); + }).pos(-239,-24).id("mods-list-btn"_spr).parent(node); + }).id("gdutils-menu"_spr).parent(this); + return true; + } +}; diff --git a/test/src/main.cpp b/test/src/main.cpp index 25ad723..c4e7b2e 100644 --- a/test/src/main.cpp +++ b/test/src/main.cpp @@ -39,7 +39,7 @@ class $modify(MenuLayer) { GDUtils::Events::OnServerConnect().listen([](bool const& connected) { Loader::get()->queueInMainThread([connected]() { if (connected) { - log::info("I am connected to the notifiaction servers!"); + log::info("I am connected to the notification servers!"); } }); return ListenerResult::Propagate; From 1d9e358cfb7891ca564a7ee1cd2d121fdbcb789b Mon Sep 17 00:00:00 2001 From: FireMario211 <17692105+FireMario211@users.noreply.github.com> Date: Sun, 8 Mar 2026 00:17:58 -0500 Subject: [PATCH 08/32] fix chest issue --- .gitignore | 3 +- changelog.md | 8 +++-- mod.json | 2 +- src/modules/events/DailyChest.cpp | 60 ++++++++++++++++++------------- 4 files changed, 44 insertions(+), 29 deletions(-) diff --git a/.gitignore b/.gitignore index d8108ef..b57a114 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,5 @@ build-linux/ -build-android/ -build-armv8/ +build-android*/ test/build-linux test/compile_commands.json .cache/ diff --git a/changelog.md b/changelog.md index aae1b33..dfead65 100644 --- a/changelog.md +++ b/changelog.md @@ -1,10 +1,14 @@ # Changelog +## v1.6.0-beta.2 (2026-03-08) +* Fixed chests crashing upon opening for Android. + ## v1.6.0-beta.1 (2026-03-08) * GDUtils ported to Geode 5.0.0 (GD 2.2081) -* Changed logic for chest notifications +* Changed logic for chest notifications. +* Changed locations for where GDUtils stores files. * Temporarily removed leaderboards due to not being rewritten yet. * Fixed Spotify feature not unmuting when playtesting in Editor. -* Fixed notification being clickable +* Fixed notification being clickable. ## v1.5.4 (2025-09-09) * Added coloured container animation when searching a friend on the friend list. diff --git a/mod.json b/mod.json index 933c35c..a49df9b 100644 --- a/mod.json +++ b/mod.json @@ -1,6 +1,6 @@ { "geode": "5.0.0", - "version": "v1.6.0-beta.1", + "version": "v1.6.0-beta.2", "gd": { "win": "2.2081", "mac": "2.2081", diff --git a/src/modules/events/DailyChest.cpp b/src/modules/events/DailyChest.cpp index 8f70412..98c968b 100644 --- a/src/modules/events/DailyChest.cpp +++ b/src/modules/events/DailyChest.cpp @@ -46,7 +46,6 @@ static GJRewardItem* time1 = nullptr; static GJRewardItem* time2 = nullptr; static std::optional> g_chestTx; - void DailyChest::getRewards() { auto glm = GameLevelManager::sharedState(); if (glm) { @@ -121,8 +120,40 @@ void giveChestNotify(bool large) { }); } +#include +class $modify(RULHook, RewardUnlockLayer) { + void step2() { + int tag = m_chestType; + if (g_chestTx) { + if (tag == 1) { + (void)g_chestTx->trySend({ + .type = ChestMsg::Type::Opened, + .opened = ChestKind::Small + }); + } else if (tag == 2) { + (void)g_chestTx->trySend({ + .type = ChestMsg::Type::Opened, + .opened = ChestKind::Large + }); + } + } + refreshRewards(); + RewardUnlockLayer::step2(); + } +}; + +static void toggleHook(bool value) { + for (auto& hook : geode::Mod::get()->getHooks()) { + if (hook->getDisplayName() == "RewardUnlockLayer::step2") { + (void)(value ? hook->enable() : hook->disable()); + } + } +} $on_game(Loaded) { - if (!Mod::get()->getSettingValue("largeChest") && !Mod::get()->getSettingValue("smallChest")) return; + if (!Mod::get()->getSettingValue("largeChest") && !Mod::get()->getSettingValue("smallChest")) { + toggleHook(false); + return; + } auto [tx, rx] = arc::mpsc::channel(); g_chestTx = std::move(tx); // wait 5 seconds before @@ -233,7 +264,8 @@ void giveChestNotify(bool large) { }); listenForSettingChanges("smallChest", [](bool value) { if (!value && !Mod::get()->getSettingValue("largeChest")) { - if (g_chestTx) { + if (g_chestTx) { + toggleHook(false); (void)g_chestTx->trySend({ ChestMsg::Type::Stop }); } } @@ -241,30 +273,10 @@ void giveChestNotify(bool large) { listenForSettingChanges("largeChest", [](bool value) { if (!value && !Mod::get()->getSettingValue("smallChest")) { if (g_chestTx) { + toggleHook(false); (void)g_chestTx->trySend({ ChestMsg::Type::Stop }); } } }); refreshRewards(); } -#include -class $modify(RULHook, RewardUnlockLayer) { - void step3() { - int tag = m_chestType; - if (g_chestTx) { - if (tag == 1) { - (void)g_chestTx->trySend({ - .type = ChestMsg::Type::Opened, - .opened = ChestKind::Small - }); - } else if (tag == 2) { - (void)g_chestTx->trySend({ - .type = ChestMsg::Type::Opened, - .opened = ChestKind::Large - }); - } - } - refreshRewards(); - RewardUnlockLayer::step3(); - } -}; From c39d8075f6544c489894ba26a2e2aeac0622395e Mon Sep 17 00:00:00 2001 From: FireMario211 <17692105+FireMario211@users.noreply.github.com> Date: Fri, 3 Apr 2026 08:27:04 -0400 Subject: [PATCH 09/32] add backgrounds and remove level sharing temporarily --- src/hooks/Backgrounds.cpp | 140 +++++++++++++++++++++++++++++++++++ src/hooks/InfoLayer.cpp | 45 ----------- src/hooks/LevelInfoLayer.cpp | 6 +- 3 files changed, 143 insertions(+), 48 deletions(-) create mode 100644 src/hooks/Backgrounds.cpp delete mode 100644 src/hooks/InfoLayer.cpp diff --git a/src/hooks/Backgrounds.cpp b/src/hooks/Backgrounds.cpp new file mode 100644 index 0000000..7f8c4fe --- /dev/null +++ b/src/hooks/Backgrounds.cpp @@ -0,0 +1,140 @@ +#include +#include +#include +// Backgrounds +// i dont like ios +#ifndef GEODE_IS_IOS +class $modify(UtilCCSprite, cocos2d::CCSprite) { + static CCSprite* create(char const* name) { + auto ret = CCSprite::create(name); + if (ret == nullptr) return ret; + // better code, thanks camila! + if (!strcmp(name, "GJ_gradientBG.png")) { + ret->retain(); + Loader::get()->queueInMainThread([=] { + if (ret != nullptr) { + if (ret->getColor() == ccc3(0, 102, 255)) { + auto scene = CCDirector::sharedDirector()->getRunningScene(); + if (scene->getChildrenCount() == 0) return; + auto layer = scene->getChildren()->objectAtIndex(0); + ret->setColor(Mod::get()->getSettingValue("background")); + } + ret->release(); + } + }); + } + return ret; + } +}; + +// Child background +class $modify(UtilCCScale9, cocos2d::extension::CCScale9Sprite) { + static cocos2d::extension::CCScale9Sprite* create(char const* name, CCRect rect) { + auto ret = cocos2d::extension::CCScale9Sprite::create(name, rect); + if (ret == nullptr) return ret; + // better code, thanks camila! + if ((!strcmp(name, "square02b_001.png") || !strcmp(name, "square02b_small.png"))) { + ret->retain(); + Loader::get()->queueInMainThread([=] { + if (ret != nullptr) { + // jouca waht + if (ret->getColor() == ccc3(0, 56, 141)) { + ccColor3B new_color = Mod::get()->getSettingValue("child-background"); + if (Mod::get()->getSettingValue("syncBGcolor")) { + new_color = Mod::get()->getSettingValue("background"); + } + if (Mod::get()->getSettingValue("addChildBDGradient")) { + new_color = ccc3(new_color.r * 0.79, new_color.g * 0.79, new_color.b * 0.79); + } + if (Mod::get()->getSettingValue("bgchild-transparency")) { + int opacity = Mod::get()->getSettingValue("bgchild-transparency-scroll"); + ret->setOpacity(opacity); + } + ret->setColor(new_color); + } else if (ret->getColor() == ccc3(0, 39, 98)) { + ccColor3B new_color = Mod::get()->getSettingValue("child-background"); + if (Mod::get()->getSettingValue("syncBGcolor")) { + new_color = Mod::get()->getSettingValue("background"); + } + if (Mod::get()->getSettingValue("addChildBDGradient")) { + new_color = ccc3(new_color.r * 0.71, new_color.g * 0.71, new_color.b * 0.71); + } + if (Mod::get()->getSettingValue("bgchild-transparency")) { + int opacity = Mod::get()->getSettingValue("bgchild-transparency-scroll"); + ret->setOpacity(opacity); + } + ret->setColor(new_color); + } else if (ret->getColor() == ccc3(0, 46, 117)) { + ccColor3B new_color = Mod::get()->getSettingValue("child-background"); + if (Mod::get()->getSettingValue("syncBGcolor")) { + new_color = Mod::get()->getSettingValue("background"); + } + if (Mod::get()->getSettingValue("addChildBDGradient")) { + new_color = ccc3(new_color.r * 0.74, new_color.g * 0.74, new_color.b * 0.74); + } + if (Mod::get()->getSettingValue("bgchild-transparency")) { + int opacity = Mod::get()->getSettingValue("bgchild-transparency-scroll"); + ret->setOpacity(opacity); + } + ret->setColor(new_color); + } else if (ret->getColor() == ccc3(0, 36, 91)) { + ccColor3B new_color = Mod::get()->getSettingValue("child-background"); + if (Mod::get()->getSettingValue("syncBGcolor")) { + new_color = Mod::get()->getSettingValue("background"); + } + if (Mod::get()->getSettingValue("addChildBDGradient")) { + new_color = ccc3(new_color.r * 0.70, new_color.g * 0.70, new_color.b * 0.70); + } + if (Mod::get()->getSettingValue("bgchild-transparency")) { + int opacity = Mod::get()->getSettingValue("bgchild-transparency-scroll"); + ret->setOpacity(opacity); + } + ret->setColor(new_color); + } else if (ret->getColor() == ccc3(0, 31, 79)) { + ccColor3B new_color = Mod::get()->getSettingValue("child-background"); + if (Mod::get()->getSettingValue("syncBGcolor")) { + new_color = Mod::get()->getSettingValue("background"); + } + if (Mod::get()->getSettingValue("addChildBDGradient")) { + new_color = ccc3(new_color.r * 0.67, new_color.g * 0.67, new_color.b * 0.67); + } + if (Mod::get()->getSettingValue("bgchild-transparency")) { + int opacity = Mod::get()->getSettingValue("bgchild-transparency-scroll"); + ret->setOpacity(opacity); + } + ret->setColor(new_color); + } + ret->release(); + } + }); + } + return ret; + } +}; + +static void toggleHook(bool value) { + for (auto& hook : geode::Mod::get()->getHooks()) { + if (hook->getDisplayName().starts_with("cocos2d::")) { + if (value) { + (void)hook->enable(); + } else { + (void)hook->disable(); + } + } + } +} + +$execute { + listenForSettingChanges("activate-background", [](bool value) { + log::info("Background changed to {}", value); + toggleHook(value); + }); +} + +$on_mod(Loaded) { + if (!Mod::get()->getSettingValue("activate-background")) { + toggleHook(false); + } +} + +#endif diff --git a/src/hooks/InfoLayer.cpp b/src/hooks/InfoLayer.cpp deleted file mode 100644 index 19a4164..0000000 --- a/src/hooks/InfoLayer.cpp +++ /dev/null @@ -1,45 +0,0 @@ -#include -#include - -// Copy button for sharing levels -class $modify(ProtoInfoLayer, InfoLayer) { - bool init(GJGameLevel* level, GJUserScore* userscore, GJLevelList* levellist) { - if (!InfoLayer::init(level, userscore, levellist)) return false; - if (level == nullptr) return true; - std::string levelID = std::to_string(level->m_levelID.value()); - if (auto menu = typeinfo_cast(this->getChildByIDRecursive("left-side-menu"))) { - Build::createSpriteName("GJ_redoBtn_001.png").scale(0.7f).intoMenuItem([levelID = std::move(levelID)]() { - utils::clipboard::write(fmt::format("https://gdutils.com/{}", levelID)); - auto notification = Notification::create("Copied level URL to clipboard!", NotificationIcon::Success); - notification->setTime(0.2f); - notification->show(); - }).pos(14, -55).parent(menu); - } - return true; - } -}; - -static void toggleHook(bool value) { - for (auto& hook : geode::Mod::get()->getHooks()) { - if (hook->getDisplayName() == "InfoLayer::init") { - if (value) { - (void)hook->enable(); - } else { - (void)hook->disable(); - } - } - } -} - -$execute { - listenForSettingChanges("levelsProtocolButton", [](bool value) { - log::info("Level Protocols Button changed to {}", value); - toggleHook(value); - }); -} - -$on_mod(Loaded) { - if (!Mod::get()->getSettingValue("levelsProtocolButton")) { - toggleHook(false); - } -} diff --git a/src/hooks/LevelInfoLayer.cpp b/src/hooks/LevelInfoLayer.cpp index 65378d2..0806a60 100644 --- a/src/hooks/LevelInfoLayer.cpp +++ b/src/hooks/LevelInfoLayer.cpp @@ -88,7 +88,7 @@ class $modify(CustomLevelInfoLayer, LevelInfoLayer) { url = fmt::format("https://api.aredl.net/api/aredl/levels/{}", levelID); } geode::utils::web::WebRequest request = web::WebRequest(); - m_fields->m_listener.spawn("@loaderDemonListLevelInfo", request.get(url.c_str()), [this, loading_circle, positionLabel, demonSpr, level, levelID](web::WebResponse value) { + m_fields->m_listener.spawn("@loaderDemonListLevelInfo", request.get(url.c_str()), [this, loading_circle, positionLabel, demonSpr, levelID](web::WebResponse value) { if (value.ok()) { matjson::Value json = value.json().unwrapOrDefault(); @@ -112,7 +112,7 @@ class $modify(CustomLevelInfoLayer, LevelInfoLayer) { positionLabel->setVisible(true); demonSpr->setVisible(true); set(levelID, position); - log::info("Level found in Pointercrate! {} at #{}", level->m_levelName.c_str(), position); + log::info("Level found in Pointercrate! {} at #{}", levelID, position); } } @@ -132,7 +132,7 @@ class $modify(CustomLevelInfoLayer, LevelInfoLayer) { positionLabel->setVisible(true); demonSpr->setVisible(true); set(levelID, position); - log::info("Level found in AREDL! {} at #{}", level->m_levelName.c_str(), position); + log::info("Level found in AREDL! {} at #{}", levelID, position); } } } From 7712f460890e3df77b8093d2bc628a9a845e1e58 Mon Sep 17 00:00:00 2001 From: Cvolton Date: Sat, 11 Apr 2026 20:33:33 +0200 Subject: [PATCH 10/32] Fix image URL formatting in about.md (#69) --- about.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/about.md b/about.md index 1c61fc7..5216488 100644 --- a/about.md +++ b/about.md @@ -2,7 +2,7 @@ **by [Jouca](user:7026949) and [Firee](user:6253758)** A Geometry Dash Mod for adding more tools such as Notifications, auto-song mute, game fixes & more! -![Mod Example](gdutilsdevs.gdutils/Screenshot_1.png&scale:0.8) +![Mod Example](gdutilsdevs.gdutils/Screenshot_1.png?scale:0.8) # Need help? * You can join our [Discord server](https://discord.gg/MU4Rpc6xbJ) for **any assistance** or about **next updates**, **sneak peeks** & **more**! From 34fb167a9eb4cab9301e681740efe1e78634b42c Mon Sep 17 00:00:00 2001 From: FireMario211 <17692105+FireMario211@users.noreply.github.com> Date: Sun, 19 Apr 2026 14:02:22 -0400 Subject: [PATCH 11/32] push current changes (unfinished) --- src/hooks/CustomBadges.cpp | 75 ++++++++++++++++++++----------- src/modules/events/DailyChest.cpp | 8 ++-- 2 files changed, 53 insertions(+), 30 deletions(-) diff --git a/src/hooks/CustomBadges.cpp b/src/hooks/CustomBadges.cpp index 2e50da5..e666946 100644 --- a/src/hooks/CustomBadges.cpp +++ b/src/hooks/CustomBadges.cpp @@ -6,6 +6,19 @@ #include std::string s_cachedBadgesResponse = ""; +struct BadgeInfo { + std::string title; + std::string desc; + bool isKofi; + float width; +}; +std::unordered_map s_cachedBadgeInfo; + +static void cacheBadgeInfo(std::string key, BadgeInfo info) { + if (!s_cachedBadgeInfo.contains(key)) { + s_cachedBadgeInfo[key] = info; + } +} static void requestBadges(async::TaskHolder listener, std::function callback) { if (!s_cachedBadgesResponse.empty()) { @@ -288,11 +301,38 @@ int hexStringToInt(const std::string& hexStr) { return value; } -class $modify(CommentCell) { +class $modify(CustomBadgesCommentCell, CommentCell) { struct Fields { async::TaskHolder m_listener; }; + void onBadgeClick(CCObject* ret) { + if (!s_cachedBadgeInfo.contains(static_cast(ret)->getID())) return; + auto info = s_cachedBadgeInfo[static_cast(ret)->getID()]; + if (info.isKofi) { + geode::createQuickPopup( + info.title.c_str(), + geode::utils::string::replace(info.desc, "\\n", "\n").c_str(), + "OK", "Ko-Fi", + [](auto, bool btn2) { + if (btn2) { + geode::utils::web::openLinkInBrowser("https://ko-fi.com/gdutils"); + } + }, + info.width + ); + } else { + FLAlertLayer::create( + nullptr, + info.title.c_str(), + info.desc.c_str(), + "OK", + nullptr, + info.width + )->show(); + } + } + void handleResponse(std::string result, int commentID) { auto menu = m_mainLayer->getChildByIDRecursive("username-menu"); if (!menu) return; @@ -353,31 +393,14 @@ class $modify(CommentCell) { if (accountID_data == accountID) { if (!m_mainLayer->getChildByIDRecursive(badge_id)) { - auto btn = Build::createSpriteName(fmt::format("{}"_spr, badge_sprite).c_str()).scale(badge_scale).intoMenuItem([=]() { - if (isKofi) { - geode::createQuickPopup( - alertTitle.c_str(), - geode::utils::string::replace(alertDesc, "\\n", "\n").c_str(), - "OK", "Ko-Fi", - [](auto, bool btn2) { - if (btn2) { - geode::utils::web::openLinkInBrowser("https://ko-fi.com/gdutils"); - } - }, - alertWidth - ); - } else { - FLAlertLayer::create( - nullptr, - alertTitle.c_str(), - alertDesc.c_str(), - "OK", - nullptr, - alertWidth - )->show(); - } - }).id(badge_id).collect(); - + // TODO: how to recreate (issue still happens with RobTop too), search Zoink, go through comments tab, tap badges (to show more badges) and tap on one of the badges in the menu, crash because apparently m_callback is null, probably because badges api issue + cacheBadgeInfo(badge_id, { + .title = alertTitle, + .desc = alertDesc, + .isKofi = isKofi, + .width = alertWidth + }); + auto btn = Build::createSpriteName(fmt::format("{}"_spr, badge_sprite).c_str()).scale(badge_scale).intoMenuItem(this, menu_selector(CustomBadgesCommentCell::onBadgeClick)).id(badge_id).collect(); if (auto textArea = cell->getChildByType(0)) { CCArrayExt children = textArea->m_label->getChildren(); for (auto label : children) { diff --git a/src/modules/events/DailyChest.cpp b/src/modules/events/DailyChest.cpp index 98c968b..58efc2c 100644 --- a/src/modules/events/DailyChest.cpp +++ b/src/modules/events/DailyChest.cpp @@ -120,9 +120,10 @@ void giveChestNotify(bool large) { }); } +//TODO:TEST #include class $modify(RULHook, RewardUnlockLayer) { - void step2() { + void playRewardEffect() { int tag = m_chestType; if (g_chestTx) { if (tag == 1) { @@ -137,14 +138,13 @@ class $modify(RULHook, RewardUnlockLayer) { }); } } - refreshRewards(); - RewardUnlockLayer::step2(); + RewardUnlockLayer::playRewardEffect(); } }; static void toggleHook(bool value) { for (auto& hook : geode::Mod::get()->getHooks()) { - if (hook->getDisplayName() == "RewardUnlockLayer::step2") { + if (hook->getDisplayName() == "RewardUnlockLayer::playRewardEffect") { (void)(value ? hook->enable() : hook->disable()); } } From ac0b50aed7c5a63b7c2daf97e40b1f1e55cfc380 Mon Sep 17 00:00:00 2001 From: Jouca Date: Wed, 22 Apr 2026 09:42:40 +0200 Subject: [PATCH 12/32] update(rewrite): Fixed badge crash --- .vscode/launch.json | 23 ++++-- CMakeLists.txt | 4 + mod.json | 2 +- src/hooks/CustomBadges.cpp | 162 +++++++++++++++++++------------------ 4 files changed, 106 insertions(+), 85 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index f43b325..a68e325 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,11 +1,8 @@ { - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ { - "name": "(Windows) Launch", + "name": "Launch GD (Debug)", "type": "cppvsdbg", "request": "launch", "program": "C:\\Program Files (x86)\\Steam\\steamapps\\common\\Geometry Dash\\GeometryDash.exe", @@ -13,7 +10,21 @@ "stopAtEntry": false, "cwd": "C:\\Program Files (x86)\\Steam\\steamapps\\common\\Geometry Dash\\", "environment": [], - "console": "internalConsole" + "console": "internalConsole", + "symbolSearchPath": "${workspaceFolder}\\build", + "sourceFileMap": { + "C:/Users/proga/Desktop/Codes/Geode projets/GDUtils-Geode": "${workspaceFolder}" + } + }, + { + "name": "Attach to GD", + "type": "cppvsdbg", + "request": "attach", + "processId": "${command:pickProcess}", + "symbolSearchPath": "${workspaceFolder}\\build", + "sourceFileMap": { + "C:/Users/proga/Desktop/Codes/Geode projets/GDUtils-Geode": "${workspaceFolder}" + } } ] -} \ No newline at end of file +} diff --git a/CMakeLists.txt b/CMakeLists.txt index 5e61ed3..1ea23b1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,6 +31,10 @@ set(BUILD_TOOLS_DOCS OFF) #set(INSTALL_STATIC_LIBS OFF) CPMAddPackage("gh:alanxz/rabbitmq-c#035eaa8") +if (WIN32) + target_compile_options(rabbitmq-static PRIVATE -Wno-incompatible-pointer-types) +endif() + set(BUILD_TESTING OFF) set(BUILD_BENCH OFF) set(BUILD_SHARED_LIBS OFF) diff --git a/mod.json b/mod.json index a49df9b..1ded1e6 100644 --- a/mod.json +++ b/mod.json @@ -1,5 +1,5 @@ { - "geode": "5.0.0", + "geode": "5.6.1", "version": "v1.6.0-beta.2", "gd": { "win": "2.2081", diff --git a/src/hooks/CustomBadges.cpp b/src/hooks/CustomBadges.cpp index e666946..a260818 100644 --- a/src/hooks/CustomBadges.cpp +++ b/src/hooks/CustomBadges.cpp @@ -6,18 +6,64 @@ #include std::string s_cachedBadgesResponse = ""; -struct BadgeInfo { - std::string title; - std::string desc; - bool isKofi; - float width; -}; -std::unordered_map s_cachedBadgeInfo; -static void cacheBadgeInfo(std::string key, BadgeInfo info) { - if (!s_cachedBadgeInfo.contains(key)) { - s_cachedBadgeInfo[key] = info; +// Primary dispatch: unique int tag -> callback. +// Each call to makeBadgeButton gets a fresh tag stored directly on the button. +// Tags are never copied by BadgesAPI (its CCMenuItemSpriteExtra::create starts with kCCNodeTagInvalid = -1). +static int s_nextBadgeTag = 0; +static std::unordered_map> s_tagToCallback; + +// Fallback dispatch: CCSpriteFrame* -> tag, used when BadgesAPI copies our button. +// BadgesAPI does CCSprite::createWithSpriteFrame(originalFrame), so the new sprite +// shares the same CCSpriteFrame object and the lookup resolves to the last registered tag. +static std::unordered_map s_frameToTag; + +// Singleton target for all badge selectors. +// IMPORTANT: BadgesAPI copies our buttons as: +// CCMenuItemSpriteExtra::create(child->getNormalImage(), this/*BadgesAPI*/, child->m_pfnSelector) +// so `this` inside onBadgeClick may be the BadgesAPI object — NEVER dereference it. +// Only touch `sender` and global/static state. +class GDUtilsBadgeHandler : public CCObject { +public: + static GDUtilsBadgeHandler* get() { + static auto* inst = [] { + auto* h = new GDUtilsBadgeHandler(); + h->retain(); + return h; + }(); + return inst; + } + + void onBadgeClick(CCObject* sender) { + auto* btn = typeinfo_cast(sender); + if (!btn) return; + + // Primary: tag set directly on this button instance (original button click). + int tag = btn->getTag(); + if (auto it = s_tagToCallback.find(tag); it != s_tagToCallback.end()) { + it->second(); + return; + } + + // Fallback: sprite frame lookup (BadgesAPI popup copy, tag is -1). + if (auto* spr = typeinfo_cast(btn->getNormalImage())) { + if (auto fit = s_frameToTag.find(spr->displayFrame()); fit != s_frameToTag.end()) { + if (auto it = s_tagToCallback.find(fit->second); it != s_tagToCallback.end()) + it->second(); + } + } } +}; + +static CCMenuItemSpriteExtra* makeBadgeButton(CCNode* normalSprite, std::string const& id, std::function callback) { + int tag = ++s_nextBadgeTag; + s_tagToCallback[tag] = std::move(callback); + if (auto* spr = typeinfo_cast(normalSprite)) + s_frameToTag[spr->displayFrame()] = tag; + auto* btn = CCMenuItemSpriteExtra::create(normalSprite, GDUtilsBadgeHandler::get(), menu_selector(GDUtilsBadgeHandler::onBadgeClick)); + btn->setID(id); + btn->setTag(tag); + return btn; } static void requestBadges(async::TaskHolder listener, std::function callback) { @@ -221,31 +267,19 @@ class $modify(GDUtilsProfilePage, ProfilePage) { data.erase(data.begin()); } if (accountID_data == accountID) { - if (!m_mainLayer->getChildByID(badge_id)) { - Build::createSpriteName(fmt::format("{}"_spr, badge_sprite).c_str()).scale(badge_scale).intoMenuItem([=]() { + if (!m_mainLayer->getChildByIDRecursive(badge_id)) { + auto* sprite = Build::createSpriteName(fmt::format("{}"_spr, badge_sprite).c_str()) + .scale(badge_scale) + .collect(); + auto* btn = makeBadgeButton(sprite, badge_id, [title = alertTitle, desc = alertDesc, isKofi, width = alertWidth]() { if (isKofi) { - geode::createQuickPopup( - alertTitle.c_str(), - geode::utils::string::replace(alertDesc, "\\n", "\n").c_str(), - "OK", "Ko-Fi", - [](auto, bool btn2) { - if (btn2) { - geode::utils::web::openLinkInBrowser("https://ko-fi.com/gdutils"); - } - }, - alertWidth - ); + geode::createQuickPopup(title.c_str(), geode::utils::string::replace(desc, "\\n", "\n").c_str(), "OK", "Ko-Fi", + [](auto, bool btn2) { if (btn2) geode::utils::web::openLinkInBrowser("https://ko-fi.com/gdutils"); }, width); } else { - FLAlertLayer::create( - nullptr, - alertTitle.c_str(), - alertDesc.c_str(), - "OK", - nullptr, - alertWidth - )->show(); + FLAlertLayer::create(nullptr, title.c_str(), desc.c_str(), "OK", nullptr, width)->show(); } - }).id(badge_id).parent(menu); + }); + menu->addChild(btn); menu->updateLayout(); } } @@ -260,14 +294,12 @@ class $modify(GDUtilsProfilePage, ProfilePage) { if (auto modBadge = menu->getChildByID("mod-badge")) { modBadge->removeFromParentAndCleanup(true); if (score->m_userName == "RobTop" && Mod::get()->getSettingValue("customBadges")) { - Build::createSpriteName("robtop_badge.png"_spr).intoMenuItem([]() { - onRobTopBadgePressed(); - }).id("mod-badge").parent(menu); + auto* spr = Build::createSpriteName("robtop_badge.png"_spr).collect(); + menu->addChild(makeBadgeButton(spr, "mod-badge", []() { onRobTopBadgePressed(); })); } else { int badgeID = score->m_modBadge; - Build::createSpriteName(fmt::format("modBadge_0{}_001.png", badgeID).c_str()).intoMenuItem([badgeID]() { - onModBadgePressed(badgeID); - }).id("mod-badge").parent(menu); + auto* spr = Build::createSpriteName(fmt::format("modBadge_0{}_001.png", badgeID).c_str()).collect(); + menu->addChild(makeBadgeButton(spr, "mod-badge", [badgeID]() { onModBadgePressed(badgeID); })); } } if (Mod::get()->getSettingValue("customBadges")) { @@ -306,33 +338,6 @@ class $modify(CustomBadgesCommentCell, CommentCell) { async::TaskHolder m_listener; }; - void onBadgeClick(CCObject* ret) { - if (!s_cachedBadgeInfo.contains(static_cast(ret)->getID())) return; - auto info = s_cachedBadgeInfo[static_cast(ret)->getID()]; - if (info.isKofi) { - geode::createQuickPopup( - info.title.c_str(), - geode::utils::string::replace(info.desc, "\\n", "\n").c_str(), - "OK", "Ko-Fi", - [](auto, bool btn2) { - if (btn2) { - geode::utils::web::openLinkInBrowser("https://ko-fi.com/gdutils"); - } - }, - info.width - ); - } else { - FLAlertLayer::create( - nullptr, - info.title.c_str(), - info.desc.c_str(), - "OK", - nullptr, - info.width - )->show(); - } - } - void handleResponse(std::string result, int commentID) { auto menu = m_mainLayer->getChildByIDRecursive("username-menu"); if (!menu) return; @@ -393,14 +398,17 @@ class $modify(CustomBadgesCommentCell, CommentCell) { if (accountID_data == accountID) { if (!m_mainLayer->getChildByIDRecursive(badge_id)) { - // TODO: how to recreate (issue still happens with RobTop too), search Zoink, go through comments tab, tap badges (to show more badges) and tap on one of the badges in the menu, crash because apparently m_callback is null, probably because badges api issue - cacheBadgeInfo(badge_id, { - .title = alertTitle, - .desc = alertDesc, - .isKofi = isKofi, - .width = alertWidth + auto* sprite = Build::createSpriteName(fmt::format("{}"_spr, badge_sprite).c_str()) + .scale(badge_scale) + .collect(); + auto* btn = makeBadgeButton(sprite, badge_id, [title = alertTitle, desc = alertDesc, isKofi, width = alertWidth]() { + if (isKofi) { + geode::createQuickPopup(title.c_str(), geode::utils::string::replace(desc, "\\n", "\n").c_str(), "OK", "Ko-Fi", + [](auto, bool btn2) { if (btn2) geode::utils::web::openLinkInBrowser("https://ko-fi.com/gdutils"); }, width); + } else { + FLAlertLayer::create(nullptr, title.c_str(), desc.c_str(), "OK", nullptr, width)->show(); + } }); - auto btn = Build::createSpriteName(fmt::format("{}"_spr, badge_sprite).c_str()).scale(badge_scale).intoMenuItem(this, menu_selector(CustomBadgesCommentCell::onBadgeClick)).id(badge_id).collect(); if (auto textArea = cell->getChildByType(0)) { CCArrayExt children = textArea->m_label->getChildren(); for (auto label : children) { @@ -437,14 +445,12 @@ class $modify(CustomBadgesCommentCell, CommentCell) { modBadge->removeFromParentAndCleanup(true); CCMenuItemSpriteExtra* badgeBtn; if (m_comment->m_userName == "RobTop" && Mod::get()->getSettingValue("customBadges")) { - Build::createSpriteName("robtop_badge.png"_spr).scale(0.75f).intoMenuItem([]() { - onRobTopBadgePressed(); - }).id("mod-badge").store(badgeBtn); + auto* spr = Build::createSpriteName("robtop_badge.png"_spr).scale(0.75f).collect(); + badgeBtn = makeBadgeButton(spr, "mod-badge", []() { onRobTopBadgePressed(); }); } else { int badgeID = comment->m_modBadge; - Build::createSpriteName(fmt::format("modBadge_0{}_001.png", badgeID).c_str()).scale(0.6f).intoMenuItem([badgeID]() { - onModBadgePressed(badgeID); - }).id("mod-badge").store(badgeBtn); + auto* spr = Build::createSpriteName(fmt::format("modBadge_0{}_001.png", badgeID).c_str()).scale(0.6f).collect(); + badgeBtn = makeBadgeButton(spr, "mod-badge", [badgeID]() { onModBadgePressed(badgeID); }); } if (getChildByIDRecursive("percentage-label")) { menu->insertBefore(badgeBtn, getChildByIDRecursive("percentage-label")); From 563b76cab78eed5dcc6163b492902631331a73f8 Mon Sep 17 00:00:00 2001 From: FireMario211 <17692105+FireMario211@users.noreply.github.com> Date: Wed, 22 Apr 2026 05:51:52 -0400 Subject: [PATCH 13/32] add refresh rewards --- mod.json | 12 ------------ src/modules/events/DailyChest.cpp | 3 ++- 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/mod.json b/mod.json index 1ded1e6..6dcd603 100644 --- a/mod.json +++ b/mod.json @@ -300,18 +300,6 @@ "default": "Pointercrate", "one-of": ["Pointercrate", "AreDL"] }, - "levelsProtocol": { - "name": "Allow Levels Protocol", - "description": "Whether or not to allow Geometry Dash to redirect to a level if you were to use a https://gdutils.com/ url, or gdutils: protocol.", - "type": "bool", - "default": true - }, - "levelsProtocolButton": { - "name": "Share level URL Button", - "description": "Whether or not to show the Share Level URL button in the Level Info Menu.", - "type": "bool", - "default": true - }, "socketServer": { "name": "Notifications Server", "description": "Allows you to receive rate notifications. (Recommended to keep enabled)\n\nRestart the game to apply changes.", diff --git a/src/modules/events/DailyChest.cpp b/src/modules/events/DailyChest.cpp index 58efc2c..063f1a5 100644 --- a/src/modules/events/DailyChest.cpp +++ b/src/modules/events/DailyChest.cpp @@ -120,7 +120,7 @@ void giveChestNotify(bool large) { }); } -//TODO:TEST +// TODO: test RewardUnlockLayer::playRewardEffect to fix mod menus from skipping step2 #include class $modify(RULHook, RewardUnlockLayer) { void playRewardEffect() { @@ -138,6 +138,7 @@ class $modify(RULHook, RewardUnlockLayer) { }); } } + refreshRewards(); RewardUnlockLayer::playRewardEffect(); } }; From 681388b8650e30eb2a2423be24098541f13740dc Mon Sep 17 00:00:00 2001 From: FireMario211 <17692105+FireMario211@users.noreply.github.com> Date: Wed, 22 Apr 2026 05:52:20 -0400 Subject: [PATCH 14/32] fix mod from not compiling on actions --- test/mod.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/mod.json b/test/mod.json index 4261b25..f15f939 100644 --- a/test/mod.json +++ b/test/mod.json @@ -1,5 +1,5 @@ { - "geode": "5.0.0", + "geode": "5.6.1", "gd": { "win": "2.2081", "android": "2.2081", From ce40f227d77c31d7d712377158f27ecb48f6e26a Mon Sep 17 00:00:00 2001 From: Jouca Date: Wed, 22 Apr 2026 13:39:49 +0200 Subject: [PATCH 15/32] update(rewrite): Add back MoreLeaderboards --- src/hooks/CustomScoreCell.cpp | 202 ++++ src/hooks/LeaderboardsLayer.cpp | 30 +- src/ui/moreleaderboards/LevelHelper.hpp | 159 ++++ src/ui/moreleaderboards/MoreLeaderboards.cpp | 863 ++++++++++++++++++ src/ui/moreleaderboards/MoreLeaderboards.hpp | 118 +++ .../mods/MoreLeaderboardsCell.cpp | 134 +++ .../mods/MoreLeaderboardsCell.hpp | 15 + .../mods/MoreLeaderboardsListView.cpp | 65 ++ .../mods/MoreLeaderboardsListView.hpp | 12 + .../moreleaderboards/region/SelectRegion.cpp | 66 ++ .../moreleaderboards/region/SelectRegion.hpp | 25 + .../region/SelectRegionCell.cpp | 80 ++ .../region/SelectRegionCell.hpp | 18 + src/ui/moreleaderboards/tabs/TabButton.cpp | 114 +++ src/ui/moreleaderboards/tabs/TabButton.hpp | 43 + 15 files changed, 1934 insertions(+), 10 deletions(-) create mode 100644 src/hooks/CustomScoreCell.cpp create mode 100644 src/ui/moreleaderboards/LevelHelper.hpp create mode 100644 src/ui/moreleaderboards/MoreLeaderboards.cpp create mode 100644 src/ui/moreleaderboards/MoreLeaderboards.hpp create mode 100644 src/ui/moreleaderboards/mods/MoreLeaderboardsCell.cpp create mode 100644 src/ui/moreleaderboards/mods/MoreLeaderboardsCell.hpp create mode 100644 src/ui/moreleaderboards/mods/MoreLeaderboardsListView.cpp create mode 100644 src/ui/moreleaderboards/mods/MoreLeaderboardsListView.hpp create mode 100644 src/ui/moreleaderboards/region/SelectRegion.cpp create mode 100644 src/ui/moreleaderboards/region/SelectRegion.hpp create mode 100644 src/ui/moreleaderboards/region/SelectRegionCell.cpp create mode 100644 src/ui/moreleaderboards/region/SelectRegionCell.hpp create mode 100644 src/ui/moreleaderboards/tabs/TabButton.cpp create mode 100644 src/ui/moreleaderboards/tabs/TabButton.hpp diff --git a/src/hooks/CustomScoreCell.cpp b/src/hooks/CustomScoreCell.cpp new file mode 100644 index 0000000..0734709 --- /dev/null +++ b/src/hooks/CustomScoreCell.cpp @@ -0,0 +1,202 @@ +#include +#include +#include +#include + +class $modify(CustomScoreCell, GJScoreCell) { + void loadFromScore(GJUserScore* score) { + GJScoreCell::loadFromScore(score); + + CCLayer* layer = static_cast(this->getChildren()->objectAtIndex(1)); + if (!layer) return; + + std::string data_flag = MoreLeaderboards::data_response_moreLB; + if (data_flag == "") return; + + RowLayout* layout = RowLayout::create(); + layout->setGap(3); + layout->setAxisAlignment(AxisAlignment::Start); + layout->setCrossAxisOverflow(true); + + CCMenu* menu = CCMenu::create(); + menu->setContentSize(CCSize(283, 21.580f)); + menu->setPosition(206, 42); + + auto starsLabel = static_cast(layer->getChildByIDRecursive("stars-label")); + starsLabel->setLayoutOptions(AxisLayoutOptions::create()->setAutoScale(true)->setScaleLimits(0, 0.5F)->setScalePriority(1)); + auto starsIcon = static_cast(layer->getChildByIDRecursive("stars-icon")); + starsIcon->setLayoutOptions(AxisLayoutOptions::create()->setAutoScale(true)->setScaleLimits(0, 0.5F)->setScalePriority(1)); + auto moonsLabel = static_cast(layer->getChildByIDRecursive("moons-label")); + moonsLabel->setLayoutOptions(AxisLayoutOptions::create()->setAutoScale(true)->setScaleLimits(0, 0.5F)->setScalePriority(1)); + auto moonsIcon = static_cast(layer->getChildByIDRecursive("moons-icon")); + moonsIcon->setLayoutOptions(AxisLayoutOptions::create()->setAutoScale(true)->setScaleLimits(0, 0.5F)->setScalePriority(1)); + + auto* playerNameItem = static_cast(layer->getChildByIDRecursive("player-name")); + std::string playerName = static_cast(playerNameItem->getChildren()->objectAtIndex(0))->getString(); + CCMenu* menu_playername = CCMenu::create(); + menu_playername->setContentSize(playerNameItem->getContentSize()); + menu_playername->setLayout(RowLayout::create()->setGap(3)->setAxisAlignment(AxisAlignment::Start)); + + auto playerNameBM = CCLabelBMFont::create(playerName.c_str(), "goldFont.fnt"); + auto playerNameBtn = CCMenuItemSpriteExtra::create( + playerNameBM, this, (SEL_MenuHandler)&CustomScoreCell::onViewProfile + ); + playerNameBtn->setLayoutOptions(AxisLayoutOptions::create()->setAutoScale(true)->setScaleLimits(0.5F, 0.8F)->setScalePriority(1)); + + std::vector dataString = MoreLeaderboards::getWords(data_flag, "#"); + std::vector users = MoreLeaderboards::getWords(dataString[0], "|"); + std::string count_diff = "0"; + + while (users.size() > 0) { + std::vector data_info = MoreLeaderboards::getWords(users[0], ":"); + while (data_info.size() > 0) { + std::string id = data_info[0]; + std::string info = data_info[1]; + if (id == "1") { + if (info != playerName) break; + } + if (id == "9989") { + count_diff = info; + } + if (id == "9990") { + if (info == "XX") break; + auto flagSpr = CCSprite::createWithSpriteFrameName((""_spr + info + ".png").c_str()); + flagSpr->setPosition({20, 15}); + flagSpr->setScaleY(20 / flagSpr->getContentHeight() * 0.50); + flagSpr->setScaleX(std::min(1.0f, 30 / flagSpr->getContentWidth())); + flagSpr->setLayoutOptions(AxisLayoutOptions::create()->setAutoScale(true)->setScalePriority(1)); + menu_playername->addChild(flagSpr); + } + data_info.erase(data_info.begin()); + data_info.erase(data_info.begin()); + } + users.erase(users.begin()); + } + + menu_playername->addChild(playerNameBtn); + menu_playername->updateLayout(); + + if (auto n = layer->getChildByID("stars-label")) n->removeFromParentAndCleanup(true); + if (auto n = layer->getChildByID("stars-icon")) n->removeFromParentAndCleanup(true); + if (auto n = layer->getChildByID("moons-label")) n->removeFromParentAndCleanup(true); + if (auto n = layer->getChildByID("moons-icon")) n->removeFromParentAndCleanup(true); + if (auto n = layer->getChildByID("main-menu")) n->removeFromParentAndCleanup(true); + + menu->addChild(menu_playername); + menu->addChild(starsLabel); + menu->addChild(starsIcon); + menu->addChild(moonsLabel); + menu->addChild(moonsIcon); + menu->setLayout(layout); + menu->updateLayout(); + + // find the main layer with stats + CCLayer* mainLayer = nullptr; + for (auto* child : this->getChildrenExt()) { + auto* search = typeinfo_cast(child); + if (search && search->getChildrenCount() > 2) { + mainLayer = search; + break; + } + } + if (!mainLayer) { layer->addChild(menu); return; } + auto statsMenu = mainLayer->getChildByIDRecursive("stats-menu"); + + if (!Loader::get()->isModLoaded("itzkiba.better_progression") && MoreLeaderboards::g_tab == StatsListType::BetterProgression) { + int currentEXP = LevelHelper::calculateTotalEXP(score); + int currentLevel = LevelHelper::getLevelFromEXP(currentEXP); + auto badgeSpr = SpriteHelper::createFullSprite(currentLevel); + badgeSpr->setPosition({75, 17.5}); + badgeSpr->setScale(0.62); + mainLayer->addChild(badgeSpr); + statsMenu->setScale(0.83); + statsMenu->setPositionX(statsMenu->getPositionX() + 13.f); + } + + auto makeClassicSpr = [](const char* base) { + auto s = CCSprite::createWithSpriteFrameName(base); + auto c = CCSprite::createWithSpriteFrameName("star_small01_001.png"); + c->setPositionX(30); c->setScale(1.2f); + s->addChild(c); + return s; + }; + auto makePlatformerSpr = [](const char* base) { + auto s = CCSprite::createWithSpriteFrameName(base); + auto m = CCSprite::createWithSpriteFrameName("moon_small01_001.png"); + m->setPositionX(30); m->setScale(1.2f); + s->addChild(m); + return s; + }; + auto pointercrate = CCSprite::createWithSpriteFrameName("diffIcon_06_btn_001.png"); + auto pc_text = CCLabelBMFont::create("PC", "goldFont.fnt"); + pc_text->setPosition(28, 0); pc_text->setScale(0.5f); + pointercrate->addChild(pc_text); + + if (statsMenu) { + const struct { const char* id; int z; } zmap[] = { + {"diamonds-label", 0}, {"diamonds-icon", 1}, + {"coins-label", 2}, {"coins-icon", 3}, + {"user-coins-label", 4}, {"user-coins-icon", 5}, + {"demons-label", 6}, {"demons-icon", 7}, + {"creator-points-label", 8}, {"creator-points-icon", 9}, + }; + for (auto& e : zmap) { + if (auto n = statsMenu->getChildByIDRecursive(e.id)) n->setZOrder(e.z); + } + } + + auto demon_icon = mainLayer->getChildByIDRecursive("demons-icon"); + auto demon_label = mainLayer->getChildByIDRecursive("demons-label"); + if (!demon_icon || !demon_label) { layer->addChild(menu); return; } + + auto demon_icon_pos = demon_icon->getPosition(); + auto demon_icon_scale = demon_icon->getScale(); + auto demon_icon_z = demon_icon->getZOrder(); + auto demon_icon_tag = demon_icon->getTag(); + auto demon_icon_layout = demon_icon->getLayoutOptions(); + auto demon_label_pos = demon_label->getPosition(); + auto demon_label_scale = demon_label->getScale(); + auto demon_label_z = demon_label->getZOrder(); + auto demon_label_tag = demon_label->getTag(); + auto demon_label_layout = demon_label->getLayoutOptions(); + + CCSprite* new_icon = nullptr; + switch (MoreLeaderboards::g_tab) { + case StatsListType::platformerDemonsEasy: new_icon = makePlatformerSpr("diffIcon_07_btn_001.png"); break; + case StatsListType::platformerDemonsMedium: new_icon = makePlatformerSpr("diffIcon_08_btn_001.png"); break; + case StatsListType::platformerDemonsHard: new_icon = makePlatformerSpr("diffIcon_06_btn_001.png"); break; + case StatsListType::platformerDemonsInsane: new_icon = makePlatformerSpr("diffIcon_09_btn_001.png"); break; + case StatsListType::platformerDemonsExtreme: new_icon = makePlatformerSpr("diffIcon_10_btn_001.png"); break; + case StatsListType::classicDemonsEasy: new_icon = makeClassicSpr("diffIcon_07_btn_001.png"); break; + case StatsListType::classicDemonsMedium: new_icon = makeClassicSpr("diffIcon_08_btn_001.png"); break; + case StatsListType::classicDemonsHard: new_icon = makeClassicSpr("diffIcon_06_btn_001.png"); break; + case StatsListType::classicDemonsInsane: new_icon = makeClassicSpr("diffIcon_09_btn_001.png"); break; + case StatsListType::classicDemonsExtreme: new_icon = makeClassicSpr("diffIcon_10_btn_001.png"); break; + case StatsListType::Pointercrate: new_icon = pointercrate; break; + default: break; + } + + if (new_icon && statsMenu) { + demon_icon->removeFromParent(); + new_icon->setPosition(demon_icon_pos); + new_icon->setScale(demon_icon_scale); + new_icon->setLayoutOptions(demon_icon_layout); + new_icon->setTag(demon_icon_tag); + new_icon->setZOrder(demon_icon_z); + statsMenu->addChild(new_icon); + + demon_label->removeFromParent(); + auto new_label = CCLabelBMFont::create(count_diff.c_str(), "bigFont.fnt"); + new_label->setPosition(demon_label_pos); + new_label->setScale(demon_label_scale); + new_label->setZOrder(demon_label_z); + new_label->setTag(demon_label_tag); + new_label->setLayoutOptions(demon_label_layout); + statsMenu->addChild(new_label); + statsMenu->updateLayout(); + } + + layer->addChild(menu); + layer->updateLayout(); + } +}; diff --git a/src/hooks/LeaderboardsLayer.cpp b/src/hooks/LeaderboardsLayer.cpp index a6fc123..6fb6e3b 100644 --- a/src/hooks/LeaderboardsLayer.cpp +++ b/src/hooks/LeaderboardsLayer.cpp @@ -1,19 +1,29 @@ #include #include -//#include +#include class $modify(LeaderboardsLayer) { bool init(LeaderboardType type, LeaderboardStat stat) { if (!LeaderboardsLayer::init(type, stat)) return false; - Build::create().with([](auto node) { - Build::createSpriteName("GJ_plusBtn_001.png").scale(.8f).intoMenuItem([]() { - //CCDirector::sharedDirector()->pushScene(CCTransitionFade::create(0.5, MoreLeaderboards::scene())); - FLAlertLayer::create("Notice", "This currently has not been implemented yet!\nPlease wait until the next major update for this feature to be added.", "OK")->show(); - }).pos(-239,20).id("more-leaderboards-btn"_spr).parent(node); - Build::createSpriteName("modBadge_01_001.png").scale(1.5f).intoMenuItem([]() { - FLAlertLayer::create("Notice", "This currently has not been implemented yet!\nPlease wait until the next major update for this feature to be added.", "OK")->show(); - }).pos(-239,-24).id("mods-list-btn"_spr).parent(node); - }).id("gdutils-menu"_spr).parent(this); + + auto menu = CCMenu::create(); + + Build::createSpriteName("GJ_plusBtn_001.png").scale(.8f) + .intoMenuItem([]() { + auto scene = CCScene::create(); + scene->addChild(MoreLeaderboards::create("more")); + CCDirector::sharedDirector()->pushScene(CCTransitionFade::create(0.5, scene)); + }).pos(-239, 20).id("more-leaderboards-btn"_spr).parent(menu); + + Build::createSpriteName("modBadge_01_001.png").scale(1.5f) + .intoMenuItem([]() { + auto scene = CCScene::create(); + scene->addChild(MoreLeaderboards::create("mods")); + CCDirector::sharedDirector()->pushScene(CCTransitionFade::create(0.5, scene)); + }).pos(-239, -24).id("mods-list-btn"_spr).parent(menu); + + menu->setID("gdutils-menu"_spr); + this->addChild(menu); return true; } }; diff --git a/src/ui/moreleaderboards/LevelHelper.hpp b/src/ui/moreleaderboards/LevelHelper.hpp new file mode 100644 index 0000000..c44fc81 --- /dev/null +++ b/src/ui/moreleaderboards/LevelHelper.hpp @@ -0,0 +1,159 @@ +#pragma once +#include + +#define STAR_EXP 5 +#define MOON_EXP 5 +#define DIAMOND_EXP 2 +#define GOLD_COIN_EXP 100 +#define USER_COIN_EXP 20 +#define DEMON_EXP 75 +#define CP_EXP 5000 + +class LevelHelper { +public: + inline static int calculateTotalEXP(GJUserScore* score) { + int totalEXP = 0; + totalEXP += (STAR_EXP * score->m_stars); + totalEXP += (MOON_EXP * score->m_moons); + totalEXP += (DIAMOND_EXP * score->m_diamonds); + totalEXP += (GOLD_COIN_EXP * score->m_secretCoins); + totalEXP += (USER_COIN_EXP * score->m_userCoins); + totalEXP += (DEMON_EXP * score->m_demons); + totalEXP += (CP_EXP * score->m_creatorPoints); + return totalEXP; + } + + inline static int getEXPRequiredForLevel(int level) { + return (50 * (level * level) + (50 * level)); + } + + inline static int getLevelFromEXP(int exp) { + return (sqrt(2 * exp + 25) - 5) / 10.0; + } +}; + +class SpriteHelper { +public: + inline static CCSprite* createFullSprite(int currentLevel) { + auto badgeSprite = SpriteHelper::getBadgeFromLevel(currentLevel); + badgeSprite->setID("itzkiba.better_progression/tier-badge"); + badgeSprite->setZOrder(10); + + auto badgeLabel = CCLabelBMFont::create(std::to_string(currentLevel).c_str(), "bigFont.fnt"); + badgeLabel->setZOrder(11); + badgeLabel->setScale((currentLevel >= 100 ? 0.3 : 0.4)); + badgeLabel->setPosition({badgeSprite->getContentSize().width / 2, badgeSprite->getContentSize().height / 2 + 1.f}); + badgeSprite->addChild(badgeLabel); + + auto badgeShadow = CCSprite::createWithSpriteFrameName("itzkiba.better_progression/shadow.png"); + badgeShadow->setZOrder(-5); + badgeShadow->setScale(0.9); + badgeShadow->setPosition({badgeSprite->getContentSize().width / 2, badgeSprite->getContentSize().height / 2}); + badgeShadow->setOpacity(100); + badgeSprite->addChild(badgeShadow); + + auto badgeGlow = SpriteHelper::getGlowFromLevel(currentLevel); + if (badgeGlow != nullptr && currentLevel <= 350) { + badgeGlow->setZOrder(-3); + badgeGlow->setPosition({badgeSprite->getContentSize().width / 2, badgeSprite->getContentSize().height / 2}); + badgeGlow->setScaleX(0.975); + badgeGlow->setScaleY(0.95); + badgeSprite->addChild(badgeGlow); + + ccBlendFunc blending = {GL_ONE, GL_ONE}; + badgeGlow->setBlendFunc(blending); + + if (currentLevel >= 200 && currentLevel <= 224) badgeGlow->setColor({255, 156, 110}); + if (currentLevel >= 225 && currentLevel <= 249) badgeGlow->setColor({90, 255, 148}); + if (currentLevel >= 250 && currentLevel <= 274) badgeGlow->setColor({65, 239, 255}); + if (currentLevel >= 275 && currentLevel <= 299) badgeGlow->setColor({245, 108, 255}); + + auto fadein = CCEaseSineInOut::create(CCFadeTo::create(0.9f, 50)); + auto fadeout = CCEaseSineInOut::create(CCFadeTo::create(0.9f, 255)); + auto sequence = CCSequence::create(fadein, fadeout, nullptr); + badgeGlow->runAction(CCRepeatForever::create(sequence)); + } + return badgeSprite; + } + + inline static CCSprite* getBadgeFromLevel(int level) { + const char* frame = "itzkiba.better_progression/tier0_0.png"; + if (level >= 5 && level <= 9) frame = "itzkiba.better_progression/tier0_1.png"; + if (level >= 10 && level <= 14) frame = "itzkiba.better_progression/tier0_2.png"; + if (level >= 15 && level <= 19) frame = "itzkiba.better_progression/tier0_3.png"; + if (level >= 20 && level <= 24) frame = "itzkiba.better_progression/tier0_4.png"; + if (level >= 25 && level <= 29) frame = "itzkiba.better_progression/tier1_0.png"; + if (level >= 30 && level <= 34) frame = "itzkiba.better_progression/tier1_1.png"; + if (level >= 35 && level <= 39) frame = "itzkiba.better_progression/tier1_2.png"; + if (level >= 40 && level <= 44) frame = "itzkiba.better_progression/tier1_3.png"; + if (level >= 45 && level <= 49) frame = "itzkiba.better_progression/tier1_4.png"; + if (level >= 50 && level <= 54) frame = "itzkiba.better_progression/tier2_0.png"; + if (level >= 55 && level <= 59) frame = "itzkiba.better_progression/tier2_1.png"; + if (level >= 60 && level <= 64) frame = "itzkiba.better_progression/tier2_2.png"; + if (level >= 65 && level <= 69) frame = "itzkiba.better_progression/tier2_3.png"; + if (level >= 70 && level <= 74) frame = "itzkiba.better_progression/tier2_4.png"; + if (level >= 75 && level <= 79) frame = "itzkiba.better_progression/tier3_0.png"; + if (level >= 80 && level <= 84) frame = "itzkiba.better_progression/tier3_1.png"; + if (level >= 85 && level <= 89) frame = "itzkiba.better_progression/tier3_2.png"; + if (level >= 90 && level <= 94) frame = "itzkiba.better_progression/tier3_3.png"; + if (level >= 95 && level <= 99) frame = "itzkiba.better_progression/tier3_4.png"; + if (level >= 100 && level <= 104) frame = "itzkiba.better_progression/tier4_0.png"; + if (level >= 105 && level <= 109) frame = "itzkiba.better_progression/tier4_1.png"; + if (level >= 110 && level <= 114) frame = "itzkiba.better_progression/tier4_2.png"; + if (level >= 115 && level <= 119) frame = "itzkiba.better_progression/tier4_3.png"; + if (level >= 120 && level <= 124) frame = "itzkiba.better_progression/tier4_4.png"; + if (level >= 125 && level <= 129) frame = "itzkiba.better_progression/tier5_0.png"; + if (level >= 130 && level <= 134) frame = "itzkiba.better_progression/tier5_1.png"; + if (level >= 135 && level <= 139) frame = "itzkiba.better_progression/tier5_2.png"; + if (level >= 140 && level <= 144) frame = "itzkiba.better_progression/tier5_3.png"; + if (level >= 145 && level <= 149) frame = "itzkiba.better_progression/tier5_4.png"; + if (level >= 150 && level <= 154) frame = "itzkiba.better_progression/tier6_0.png"; + if (level >= 155 && level <= 159) frame = "itzkiba.better_progression/tier6_1.png"; + if (level >= 160 && level <= 164) frame = "itzkiba.better_progression/tier6_2.png"; + if (level >= 165 && level <= 169) frame = "itzkiba.better_progression/tier6_3.png"; + if (level >= 170 && level <= 174) frame = "itzkiba.better_progression/tier6_4.png"; + if (level >= 175 && level <= 179) frame = "itzkiba.better_progression/tier7_0.png"; + if (level >= 180 && level <= 184) frame = "itzkiba.better_progression/tier7_1.png"; + if (level >= 185 && level <= 189) frame = "itzkiba.better_progression/tier7_2.png"; + if (level >= 190 && level <= 194) frame = "itzkiba.better_progression/tier7_3.png"; + if (level >= 195 && level <= 199) frame = "itzkiba.better_progression/tier7_4.png"; + if (level >= 200 && level <= 204) frame = "itzkiba.better_progression/tier8_0.png"; + if (level >= 205 && level <= 209) frame = "itzkiba.better_progression/tier8_1.png"; + if (level >= 210 && level <= 214) frame = "itzkiba.better_progression/tier8_2.png"; + if (level >= 215 && level <= 219) frame = "itzkiba.better_progression/tier8_3.png"; + if (level >= 220 && level <= 224) frame = "itzkiba.better_progression/tier8_4.png"; + if (level >= 225 && level <= 229) frame = "itzkiba.better_progression/tier9_0.png"; + if (level >= 230 && level <= 234) frame = "itzkiba.better_progression/tier9_1.png"; + if (level >= 235 && level <= 239) frame = "itzkiba.better_progression/tier9_2.png"; + if (level >= 240 && level <= 244) frame = "itzkiba.better_progression/tier9_3.png"; + if (level >= 245 && level <= 249) frame = "itzkiba.better_progression/tier9_4.png"; + if (level >= 250 && level <= 254) frame = "itzkiba.better_progression/tier10_0.png"; + if (level >= 255 && level <= 259) frame = "itzkiba.better_progression/tier10_1.png"; + if (level >= 260 && level <= 264) frame = "itzkiba.better_progression/tier10_2.png"; + if (level >= 265 && level <= 269) frame = "itzkiba.better_progression/tier10_3.png"; + if (level >= 270 && level <= 274) frame = "itzkiba.better_progression/tier10_4.png"; + if (level >= 275 && level <= 279) frame = "itzkiba.better_progression/tier11_0.png"; + if (level >= 280 && level <= 284) frame = "itzkiba.better_progression/tier11_1.png"; + if (level >= 285 && level <= 289) frame = "itzkiba.better_progression/tier11_2.png"; + if (level >= 290 && level <= 294) frame = "itzkiba.better_progression/tier11_3.png"; + if (level >= 295 && level <= 299) frame = "itzkiba.better_progression/tier11_4.png"; + if (level >= 300 && level <= 349) frame = "itzkiba.better_progression/tier12.png"; + return CCSprite::createWithSpriteFrameName(frame); + } + + inline static CCSprite* getGlowFromLevel(int level) { + if (level < 200) return nullptr; + int mod = level % 25; + int tier = mod / 5; + const char* frames[] = { + "itzkiba.better_progression/glow_0.png", + "itzkiba.better_progression/glow_1.png", + "itzkiba.better_progression/glow_2.png", + "itzkiba.better_progression/glow_3.png", + "itzkiba.better_progression/glow_4.png", + }; + if (level >= 300 && level <= 349) return CCSprite::createWithSpriteFrameName("itzkiba.better_progression/glow_4.png"); + if (level >= 350) return nullptr; + return CCSprite::createWithSpriteFrameName(frames[tier]); + } +}; diff --git a/src/ui/moreleaderboards/MoreLeaderboards.cpp b/src/ui/moreleaderboards/MoreLeaderboards.cpp new file mode 100644 index 0000000..f7452fa --- /dev/null +++ b/src/ui/moreleaderboards/MoreLeaderboards.cpp @@ -0,0 +1,863 @@ +#include "MoreLeaderboards.hpp" +#include "tabs/TabButton.hpp" +#include +#include + +StatsListType MoreLeaderboards::g_tab = StatsListType::Stars; +bool MoreLeaderboards::modFilter = false; +int MoreLeaderboards::modFilterType = 0; +std::string MoreLeaderboards::username = ""; +int MoreLeaderboards::scroll_int = 0; +std::string MoreLeaderboards::data_response_moreLB = ""; + +static int page = 0; +static int start_count = 0; +static int end_count = 0; +static int total_count = 0; + +std::vector MoreLeaderboards::getWords(std::string s, std::string delim) { + std::vector res; + std::string token = ""; + for (int i = 0; i < (int)s.size(); i++) { + bool flag = true; + for (int j = 0; j < (int)delim.size(); j++) { + if (s[i + j] != delim[j]) flag = false; + } + if (flag) { + if (token.size() > 0) { + res.push_back(token); + token = ""; + i += delim.size() - 1; + } + } else { + token += s[i]; + } + } + res.push_back(token); + return res; +} + +class SearchUserLBLayer : public Popup { +protected: + bool init(MoreLeaderboards* layer) { + if (!Popup::init(260.f, 75.f)) return false; + m_layer = layer; + input_username->setMaxCharCount(20); + input_username->setPlaceholder(""); + m_buttonMenu->addChildAtPosition(input_username, Anchor::Center, {0, 10}); + + Build::create("Search", 60, true, "bigFont.fnt", "GJ_button_01.png", 30, .5F) + .intoMenuItem([this]() { + if (this->m_layer->loading) return; + MoreLeaderboards::username = input_username->getString(); + this->m_layer->changeTabPage(); + this->m_layer->onTab(nullptr); + Popup::onClose(nullptr); + }).parentAtPos(m_buttonMenu, Anchor::Center, {0, -30}); + + if (MoreLeaderboards::username != "") { + input_username->setString(MoreLeaderboards::username); + Build::createSpriteName("GJ_deleteBtn_001.png").scale(.45f) + .intoMenuItem([this]() { + MoreLeaderboards::username = ""; + MoreLeaderboards::scroll_int = 0; + input_username->setString(""); + this->m_layer->changeTabPage(); + this->m_layer->onTab(nullptr); + Popup::onClose(nullptr); + }).parentAtPos(m_buttonMenu, Anchor::Right, {-17, 0}); + } + return true; + } + +public: + MoreLeaderboards* m_layer = nullptr; + TextInput* input_username = TextInput::create(200.0F, "Username", "bigFont.fnt"); + + static SearchUserLBLayer* create(MoreLeaderboards* layer) { + auto ret = new SearchUserLBLayer(); + if (ret->init(layer)) { + ret->autorelease(); + return ret; + } + delete ret; + return nullptr; + } +}; + +CCDictionary* MoreLeaderboards::responseToDict(const std::string& response) { + auto dict = CCDictionary::create(); + std::stringstream responseStream(response); + std::string currentKey; + std::string keyID; + std::string playerID; + std::string username; + std::string accountID; + + GameLevelManager* glm = GameLevelManager::sharedState(); + unsigned int i = 0; + while (getline(responseStream, currentKey, ':')) { + if (i % 2 == 0) { + keyID = currentKey; + } else { + if (keyID == "2") playerID = currentKey; + else if (keyID == "16") accountID = currentKey; + else if (keyID == "1") username = currentKey; + dict->setObject(CCString::create(currentKey.c_str()), keyID); + } + i++; + } + if (playerID.length() > 0 && username.length() > 0 && accountID.length() > 0) { + glm->storeUserName( + numFromString(playerID).unwrapOrDefault(), + numFromString(accountID).unwrapOrDefault(), + username + ); + } + return dict; +} + +void MoreLeaderboards::onMoreLeaderboards(CCObject*) { + auto scene = CCScene::create(); + scene->addChild(MoreLeaderboards::create("more")); + CCDirector::sharedDirector()->pushScene(CCTransitionFade::create(0.5, scene)); +} + +void MoreLeaderboards::onModsList(CCObject*) { + auto scene = CCScene::create(); + scene->addChild(MoreLeaderboards::create("mods")); + CCDirector::sharedDirector()->pushScene(CCTransitionFade::create(0.5, scene)); +} + +void MoreLeaderboards::onRegion(CCObject*) { + SelectRegion::scene([this](std::string id) { + country_id = id; + if (loading) return; + loading = true; + loading_circle = LoadingCircle::create(); + loading_circle->setZOrder(25); + loading_circle->setParentLayer(this); + loading_circle->show(); + + if (displayedData) { + displayedData->release(); + displayedData = cocos2d::CCArray::create(); + displayedData->retain(); + } + page = 0; + if (loading) { + startLoadingMore(); + loadPageMore(); + } + resetInfos(); + }); +} + +void MoreLeaderboards::onInfo(CCObject*) { + FLAlertLayer::create( + nullptr, + "More Leaderboards", + R"text( +More Leaderboards is a feature that allows you to view more leaderboards than the ones in the game. +You can view leaderboards for Diamonds, User Coins, Demons, Moons, and Creators Points of people that are registered on Updated Leaderboards project. + +Thanks to ChiefWoof & the Helper team for providing the data for this feature! + )text", + "OK", nullptr, 400.0f + )->show(); +} + +void MoreLeaderboards::onLBInfo(CCObject*) { + geode::createQuickPopup( + "Submit your user stats", + R"text( +You can submit your user stats to the Updated Leaderboards team by joining their Discord server and following the instructions right there. + )text", + "OK", "Discord Server", + [](auto, bool btn2) { + if (btn2) web::openLinkInBrowser("https://discord.gg/mZnDm886sR"); + } + ); +} + +MoreLeaderboards* MoreLeaderboards::create(std::string type) { + auto pRet = new MoreLeaderboards(); + SelectRegionCell::country_id = "NAN"; + if (pRet && pRet->init(type)) { + pRet->autorelease(); + return pRet; + } + CC_SAFE_DELETE(pRet); + return nullptr; +} + +bool MoreLeaderboards::init(std::string type) { + auto director = cocos2d::CCDirector::sharedDirector(); + auto size = director->getWinSize(); + + displayedData = cocos2d::CCArray::create(); + displayedData->retain(); + + auto background = CCSprite::create("GJ_gradientBG.png"); + auto backgroundSize = background->getContentSize(); + background->setScaleX(size.width / backgroundSize.width); + background->setScaleY(size.height / backgroundSize.height); + background->setAnchorPoint({0, 0}); + background->setColor(Mod::get()->getSettingValue("background")); + background->setZOrder(-1); + this->addChild(background); + + auto corner_left = CCSprite::createWithSpriteFrameName("GJ_sideArt_001.png"); + auto corner_right = CCSprite::createWithSpriteFrameName("GJ_sideArt_001.png"); + corner_right->setRotation(-90); + corner_left->setPosition({35, 35}); + corner_right->setPosition({size.width - 35, 35}); + this->addChild(corner_left); + this->addChild(corner_right); + + auto backBtn = CCMenuItemSpriteExtra::create( + CCSprite::createWithSpriteFrameName("GJ_arrow_01_001.png"), + this, menu_selector(MoreLeaderboards::backButton) + ); + auto backMenu = CCMenu::create(); + backMenu->addChild(backBtn); + backMenu->setPosition({25, size.height - 25}); + this->addChild(backMenu); + + if (type == "more") { + auto menu_region = CCMenu::create(); + + auto regionSpr = CCSprite::create("earth_btn.png"_spr); + regionSpr->setScale(.8f); + auto regionBtn = CCMenuItemSpriteExtra::create( + regionSpr, this, menu_selector(MoreLeaderboards::onRegion) + ); + regionBtn->setPosition(239, -52); + menu_region->addChild(regionBtn); + + auto infoBtn = CCMenuItemSpriteExtra::create( + CCSprite::createWithSpriteFrameName("GJ_infoIcon_001.png"), + this, menu_selector(MoreLeaderboards::onInfo) + ); + infoBtn->setPosition(-239, -120); + menu_region->addChild(infoBtn); + + auto lbBtn = CCMenuItemSpriteExtra::create( + CCSprite::createWithSpriteFrameName("GJ_levelLeaderboardBtn_001.png"), + this, menu_selector(MoreLeaderboards::onLBInfo) + ); + lbBtn->setPosition(-239, -57); + menu_region->addChild(lbBtn); + + this->addChild(menu_region); + + tab_page = 0; + +#ifndef GEODE_IS_IOS + m_tabsGradientNode = CCClippingNode::create(); +#else + m_tabsGradientNode = CCNode::create(); +#endif + m_tabsGradientNode->setContentSize(this->getContentSize()); + m_tabsGradientNode->setAnchorPoint({0.5f, 0.5f}); + m_tabsGradientNode->ignoreAnchorPointForPosition(true); + m_tabsGradientNode->setZOrder(0); + m_tabsGradientNode->setScale(0.8f); +#ifndef GEODE_IS_IOS + m_tabsGradientNode->setInverted(false); + m_tabsGradientNode->setAlphaThreshold(0.7f); +#endif + + m_tabsGradientSprite = CCSprite::create("tab-gradient.png"_spr); + m_tabsGradientNode->addChild(m_tabsGradientSprite); + + m_tabsGradientStencil = CCSprite::create("tab-gradient-mask.png"_spr); + m_tabsGradientStencil->setAnchorPoint({0.f, 0.f}); + m_tabsGradientStencil->setColor({172, 255, 67}); + m_tabsGradientStencil->setZOrder(1); + m_tabsGradientStencil->setScale(0.8f); + +#ifndef GEODE_IS_IOS + m_tabsGradientNode->setStencil(m_tabsGradientStencil); +#endif + + this->addChild(m_tabsGradientNode); + this->addChild(m_tabsGradientStencil); + + changeTabPage(); + this->onTab(nullptr); + } else if (type == "mods") { + loading_circle = LoadingCircle::create(); + loading_circle->setZOrder(25); + loading_circle->setParentLayer(this); + loading_circle->show(); + startLoadingMods(); + loadPageMods(); + } + + setTouchEnabled(true); + setKeypadEnabled(true); + return true; +} + +void MoreLeaderboards::backButton(cocos2d::CCObject*) { + MoreLeaderboards::data_response_moreLB = ""; + MoreLeaderboards::g_tab = StatsListType::Stars; + MoreLeaderboards::username = ""; + MoreLeaderboards::scroll_int = 0; + MoreLeaderboards::modFilter = false; + MoreLeaderboards::modFilterType = 0; + cocos2d::CCDirector::sharedDirector()->popSceneWithTransition(0.5F, cocos2d::PopTransition::kPopTransitionFade); +} + +void MoreLeaderboards::keyBackClicked() { + backButton(CCNode::create()); +} + +void MoreLeaderboards::fadeLoadingCircle() { + if (loading_circle == nullptr) return; + loading_circle->fadeAndRemove(); +} + +void MoreLeaderboards::handle_request_mods(std::string const& data) { + if (!displayedData) { displayedData = CCArray::create(); displayedData->retain(); } + if (data != "-1") { + displayedData = CCArray::create(); + std::vector mods = getWords(data, "|"); + while (mods.size() > 0) { + CCDictionary* modDict = CCDictionary::create(); + modDict->setObject(CCString::create(mods[0]), "modstring"); + displayedData->addObject(modDict); + mods.erase(mods.begin()); + } + } + loadPageMods(); +} + +void MoreLeaderboards::startLoadingMods() { + this->retain(); + m_listener.spawn( + web::WebRequest().get("https://clarifygdps.com/gdutils/modslist.php"), + [this](web::WebResponse value) { + if (value.ok()) handle_request_mods(value.string().unwrapOrDefault()); + fadeLoadingCircle(); + this->release(); + } + ); +} + +void MoreLeaderboards::loadPageMods() { + if (listLayer != nullptr) listLayer->removeFromParentAndCleanup(true); + auto winSize = CCDirector::sharedDirector()->getWinSize(); + leaderboardView = MoreLeaderboardsListView::create(displayedData, 356.f, 220.f); + listLayer = GJListLayer::create(leaderboardView, "GD Moderators", {191, 114, 62, 255}, 356.f, 220.f, 0); + listLayer->setPosition(winSize / 2 - listLayer->getScaledContentSize() / 2 - CCPoint(0, 5)); + addChild(listLayer); +} + +static std::string tabTypeToString(StatsListType t) { + switch (t) { + case StatsListType::Diamonds: return "diamonds"; + case StatsListType::UserCoins: return "ucoins"; + case StatsListType::Demons: return "demons"; + case StatsListType::Moons: return "moons"; + case StatsListType::Creators: return "cp"; + case StatsListType::Stars: return "stars"; + case StatsListType::classicDemonsEasy: return "classicDemonsEasy"; + case StatsListType::classicDemonsMedium: return "classicDemonsMedium"; + case StatsListType::classicDemonsHard: return "classicDemonsHard"; + case StatsListType::classicDemonsInsane: return "classicDemonsInsane"; + case StatsListType::classicDemonsExtreme: return "classicDemonsExtreme"; + case StatsListType::platformerDemonsEasy: return "platformerDemonsEasy"; + case StatsListType::platformerDemonsMedium: return "platformerDemonsMedium"; + case StatsListType::platformerDemonsHard: return "platformerDemonsHard"; + case StatsListType::platformerDemonsInsane: return "platformerDemonsInsane"; + case StatsListType::platformerDemonsExtreme: return "platformerDemonsExtreme"; + case StatsListType::BetterProgression: return "betterProgression"; + case StatsListType::Pointercrate: return "pointercratePoints"; + } + return "stars"; +} + +void MoreLeaderboards::startLoadingLeaderboard() { + this->retain(); + auto type = tabTypeToString(MoreLeaderboards::g_tab); + m_lbListener.spawn( + web::WebRequest() + .param("type", type) + .param("page", page) + .param("country", country_id) + .param("username", username) + .param("mod", (modFilter ? "1" : "0")) + .param("modFilter", modFilterType) + .param("version", "1.0") + .get("https://clarifygdps.com/gdutils/moreleaderboards.php"), + [this](web::WebResponse value) { + loading = false; + auto scene = CCDirector::sharedDirector()->getRunningScene(); + auto layer = scene->getChildren()->objectAtIndex(0); + if (layer == nullptr || typeinfo_cast(layer) == nullptr) { + this->release(); + return; + } + if (!value.ok()) { + fadeLoadingCircle(); + geode::createQuickPopup("Error", + "An error occured while sending a request on our server. Please try again later.", + "OK", nullptr, + [this](auto, bool btn2) { + if (!btn2) { MoreLeaderboards::g_tab = StatsListType::Stars; keyBackClicked(); } + } + ); + this->release(); + return; + } + auto data = value.string().unwrap(); + if (data == "-1" || data.length() < 2) { + fadeLoadingCircle(); + geode::createQuickPopup("Error", + "An error occured while sending a request on our server. Please try again later.", + "OK", nullptr, + [this](auto, bool btn2) { + if (!btn2) { MoreLeaderboards::g_tab = StatsListType::Stars; keyBackClicked(); } + } + ); + this->release(); + return; + } + if (data == "-2") { + fadeLoadingCircle(); + FLAlertLayer::create(nullptr, "Error", + "User not found : " + MoreLeaderboards::username, + "OK", nullptr, 200.0F + )->show(); + MoreLeaderboards::scroll_int = 0; + loadTabPageButtons(); + this->release(); + return; + } + handle_request_more(data); + fadeLoadingCircle(); + this->release(); + } + ); +} + +void MoreLeaderboards::startLoadingMore() { + if (data_region != "") { + startLoadingLeaderboard(); + return; + } + this->retain(); + m_listener.spawn( + web::WebRequest().get("https://clarifygdps.com/gdutils/moreleaderboards_region.php"), + [this](web::WebResponse value) { + if (!value.ok()) { + loading = false; + auto scene = CCDirector::sharedDirector()->getRunningScene(); + auto layer = scene->getChildren()->objectAtIndex(0); + if (layer == nullptr || typeinfo_cast(layer) == nullptr) { + this->release(); + return; + } + fadeLoadingCircle(); + geode::createQuickPopup("Error", + "An error occured while sending a request on our server. Please try again later.", + "OK", nullptr, + [this](auto, bool btn2) { + if (!btn2) { MoreLeaderboards::g_tab = StatsListType::Stars; keyBackClicked(); } + } + ); + this->release(); + return; + } + auto data = value.string().unwrap(); + if (data != "-1") { + data_region = data; + SelectRegion::displayedData = MoreLeaderboards::getWords(data, "|"); + } + this->release(); + startLoadingLeaderboard(); + } + ); +} + +void MoreLeaderboards::handle_request_more(std::string const& data) { + if (!displayedData) { displayedData = CCArray::create(); displayedData->retain(); } + if (data != "-1") { + data_response_moreLB = data; + displayedData = CCArray::create(); + std::vector dataString = getWords(data, "#"); + std::vector users = getWords(dataString[0], "|"); + std::vector data_page = getWords(dataString[1], "|"); + + while (users.size() > 0) { + auto score = GJUserScore::create(MoreLeaderboards::responseToDict(users[0])); + displayedData->addObject(score); + users.erase(users.begin()); + } + + int id = 0; + while (data_page.size() > 0) { + int val = numFromString(data_page[0]).unwrapOrDefault(); + if (id == 0) start_count = val; + else if (id == 1) end_count = val; + else if (id == 2) total_count = val; + else if (id == 3) page = val; + else if (id == 4 && val != 0) MoreLeaderboards::scroll_int = val; + id++; + data_page.erase(data_page.begin()); + } + } + loadPageMore(); + loadPageStats(); +} + +void MoreLeaderboards::loadPageMore() { + if (listLayer != nullptr) listLayer->removeFromParentAndCleanup(true); + auto winSize = CCDirector::sharedDirector()->getWinSize(); + leaderboardViewScore = CustomListView::create(displayedData, nullptr, 220.f, 356.f, 0, BoomListType::Score, 0.0F); + listLayer = GJListLayer::create(leaderboardViewScore, nullptr, {191, 114, 62, 255}, 356.f, 220.f, 0); + listLayer->setZOrder(31); + listLayer->setPosition(winSize / 2 - listLayer->getScaledContentSize() / 2 - CCPoint(0, 5)); + addChild(listLayer); + + if (MoreLeaderboards::scroll_int != 0) { + listLayer->m_listView->m_tableView->scrollLayer((float)-INT_MAX); + listLayer->m_listView->m_tableView->scrollLayer(MoreLeaderboards::scroll_int); + } +} + +void MoreLeaderboards::onSearch(CCObject*) { + SearchUserLBLayer::create(this)->show(); +} + +void MoreLeaderboards::loadPageStats() { + auto winSize = CCDirector::sharedDirector()->getWinSize(); + + if (page_label != nullptr) page_label->removeFromParentAndCleanup(true); + if (auto n = this->getChildByIDRecursive("menu_label")) n->removeFromParentAndCleanup(true); + if (auto n = this->getChildByIDRecursive("menu_page")) n->removeFromParentAndCleanup(true); + + CCMenu* menu_label = CCMenu::create(); + menu_label->setID("menu_label"); + menu_label->setLayout( + RowLayout::create()->setAxisAlignment(AxisAlignment::Center)->setGap(10.f) + ); + menu_label->setPosition({winSize.width / 2, 12.f}); + menu_label->setContentSize({300.f, 7.f}); + + std::string fmt = fmt::format("Top {} - {} of {}", start_count, end_count, total_count); + page_label = CCLabelBMFont::create(fmt.c_str(), "goldFont.fnt"); + page_label->setZOrder(40); + page_label->setLayoutOptions( + AxisLayoutOptions::create()->setAutoScale(true)->setScaleLimits(0, 0.7F)->setScalePriority(1) + ); + menu_label->addChild(page_label); + + trophy = CCSprite::createWithSpriteFrameName("rankIcon_top10_001.png"); + trophy->setScale(.3f); + trophy->setPosition(CCPoint{-5.f, 0.f} - page_label->getScaledContentSize().width); + menu_label->addChild(trophy); + + addChild(menu_label); + menu_label->updateLayout(); + + CCMenu* menu = CCMenu::create(); + menu->setID("menu_page"); + + if (page_left != nullptr) page_left->removeFromParentAndCleanup(true); + if (page_right != nullptr) page_right->removeFromParentAndCleanup(true); + + if (page > 0) { + page_left = CCMenuItemSpriteExtra::create( + CCSprite::createWithSpriteFrameName("GJ_arrow_02_001.png"), + this, menu_selector(MoreLeaderboards::onPageLeft) + ); + page_left->setPosition(-220, 0); + menu->addChild(page_left); + } + + if (end_count < total_count) { + auto sprite = CCSprite::createWithSpriteFrameName("GJ_arrow_02_001.png"); + sprite->setFlipX(true); + page_right = CCMenuItemSpriteExtra::create( + sprite, this, menu_selector(MoreLeaderboards::onPageRight) + ); + page_right->setPosition(220, 0); + menu->addChild(page_right); + } + + addChild(menu); + loadTabPageButtons(); +} + +void MoreLeaderboards::loadTabPageButtons() { + if (tab_page_left != nullptr) { tab_page_left->removeFromParentAndCleanup(true); tab_page_left = nullptr; } + if (tab_page_right != nullptr) { tab_page_right->removeFromParentAndCleanup(true); tab_page_right = nullptr; } + if (m_search != nullptr) { m_search->removeFromParentAndCleanup(true); m_search = nullptr; } + if (mod_filter_btn != nullptr) { mod_filter_btn->removeFromParentAndCleanup(true); mod_filter_btn = nullptr; } + if (mod_filter_type_btn != nullptr) { mod_filter_type_btn->removeFromParentAndCleanup(true); mod_filter_type_btn = nullptr; } + + if (tab_page > 0) { + auto spr = CCSprite::createWithSpriteFrameName("GJ_arrow_03_001.png"); + spr->setScale(0.6f); + tab_page_left = CCMenuItemSpriteExtra::create(spr, this, menu_selector(MoreLeaderboards::onTabPageLeft)); + tab_page_left->setPosition(-184, 138); + m_menu->addChild(tab_page_left); + } + + if (tab_page < 2) { + auto spr = CCSprite::createWithSpriteFrameName("GJ_arrow_03_001.png"); + spr->setScale(0.6f); + spr->setFlipX(true); + tab_page_right = CCMenuItemSpriteExtra::create(spr, this, menu_selector(MoreLeaderboards::onTabPageRight)); + tab_page_right->setPosition(184, 138); + m_menu->addChild(tab_page_right); + } + + m_search = CCMenuItemSpriteExtra::create( + CCSprite::createWithSpriteFrameName("gj_findBtn_001.png"), + this, menu_selector(MoreLeaderboards::onSearch) + ); + m_search->setPosition(239, 104); + m_menu->addChild(m_search); + + auto mod_filter_spr = CCSprite::createWithSpriteFrameName("modBadge_01_001.png"); + mod_filter_spr->setScale(1.4f); + if (!modFilter) mod_filter_spr->setColor({100, 100, 100}); + mod_filter_btn = CCMenuItemSpriteExtra::create( + mod_filter_spr, this, menu_selector(MoreLeaderboards::onModFilter) + ); + mod_filter_btn->setPosition(239.f, 69.f); + m_menu->addChild(mod_filter_btn); + + if (modFilter) { + CCSprite* mod_filter_type_spr; + switch (modFilterType) { + case 0: { + auto s1 = CCSprite::createWithSpriteFrameName("modBadge_01_001.png"); s1->setScale(1.2f); s1->setPosition({30, 21}); + auto s2 = CCSprite::createWithSpriteFrameName("modBadge_02_001.png"); s2->setScale(1.2f); s2->setPosition({10, 21}); + auto s3 = CCSprite::createWithSpriteFrameName("modBadge_03_001.png"); s3->setScale(1.2f); s3->setPosition({20, 11}); + mod_filter_type_spr = CCSprite::create(); + mod_filter_type_spr->addChild(s1); + mod_filter_type_spr->addChild(s2); + mod_filter_type_spr->addChild(s3); + mod_filter_type_spr->setContentSize({40, 40}); + break; + } + case 1: mod_filter_type_spr = CCSprite::createWithSpriteFrameName("modBadge_01_001.png"); mod_filter_type_spr->setScale(1.4f); break; + case 2: mod_filter_type_spr = CCSprite::createWithSpriteFrameName("modBadge_02_001.png"); mod_filter_type_spr->setScale(1.4f); break; + default: mod_filter_type_spr = CCSprite::createWithSpriteFrameName("modBadge_03_001.png"); mod_filter_type_spr->setScale(1.4f); break; + } + mod_filter_type_btn = CCMenuItemSpriteExtra::create( + mod_filter_type_spr, this, menu_selector(MoreLeaderboards::onModFilterType) + ); + mod_filter_type_btn->setPosition(239.f, 34.f); + m_menu->addChild(mod_filter_type_btn); + } +} + +void MoreLeaderboards::onTabPageLeft(CCObject*) { + if (loading) return; + tab_page--; + changeTabPage(); + page = 0; + this->onTab(nullptr); +} + +void MoreLeaderboards::onTabPageRight(CCObject*) { + if (loading) return; + tab_page++; + changeTabPage(); + page = 0; + this->onTab(nullptr); +} + +void MoreLeaderboards::onModFilter(CCObject*) { + if (loading) return; + modFilter = !modFilter; + modFilterType = 0; + page = 0; + this->onTab(nullptr); +} + +void MoreLeaderboards::onModFilterType(CCObject*) { + if (loading) return; + modFilterType = (modFilterType == 3) ? 0 : modFilterType + 1; + page = 0; + this->onTab(nullptr); +} + +void MoreLeaderboards::changeTabPage() { + if (m_search != nullptr) { m_search->removeFromParentAndCleanup(true); m_search = nullptr; } + if (m_tab1 != nullptr) { m_tab1->removeFromParentAndCleanup(true); m_tab1 = nullptr; } + if (m_tab2 != nullptr) { m_tab2->removeFromParentAndCleanup(true); m_tab2 = nullptr; } + if (m_tab3 != nullptr) { m_tab3->removeFromParentAndCleanup(true); m_tab3 = nullptr; } + if (m_tab4 != nullptr) { m_tab4->removeFromParentAndCleanup(true); m_tab4 = nullptr; } + if (m_tab5 != nullptr) { m_tab5->removeFromParentAndCleanup(true); m_tab5 = nullptr; } + if (m_tab6 != nullptr) { m_tab6->removeFromParentAndCleanup(true); m_tab6 = nullptr; } + + if (auto n = this->getChildByIDRecursive("menu_gdutils_buttons")) + n->removeFromParentAndCleanup(true); + tab_page_left = nullptr; + tab_page_right = nullptr; + mod_filter_btn = nullptr; + mod_filter_type_btn = nullptr; + + m_menu = CCMenu::create(); + m_menu->setID("menu_gdutils_buttons"); + m_menu->setZOrder(1); + + auto stars_sprite = CCSprite::createWithSpriteFrameName("star_small01_001.png"); + auto moons_sprite = CCSprite::createWithSpriteFrameName("moon_small01_001.png"); + auto diamond_sprite = CCSprite::createWithSpriteFrameName("diamond_small01_001.png"); + auto usercoins_sprite = CCSprite::createWithSpriteFrameName("GJ_coinsIcon2_001.png"); + auto demons_sprite = CCSprite::createWithSpriteFrameName("GJ_demonIcon_001.png"); + auto creators_sprite = CCSprite::createWithSpriteFrameName("GJ_hammerIcon_001.png"); + auto better_progression = CCSprite::create("better_progression.png"_spr); + auto pointercrate = CCSprite::createWithSpriteFrameName("diffIcon_06_btn_001.png"); + auto pointercrate_text = CCLabelBMFont::create("PC", "goldFont.fnt"); + pointercrate_text->setPosition(28, 0); + pointercrate_text->setScale(0.5f); + pointercrate->addChild(pointercrate_text); + + auto makeClassicSpr = [](const char* base) { + auto classic = CCSprite::createWithSpriteFrameName("star_small01_001.png"); + classic->setPositionX(30); classic->setScale(1.2f); + auto spr = CCSprite::createWithSpriteFrameName(base); + spr->addChild(classic); + return spr; + }; + auto makePlatformerSpr = [](const char* base) { + auto moon = CCSprite::createWithSpriteFrameName("moon_small01_001.png"); + moon->setPositionX(30); moon->setScale(1.2f); + auto spr = CCSprite::createWithSpriteFrameName(base); + spr->addChild(moon); + return spr; + }; + + auto addTab = [&](CCMenuItemToggler*& slot, CCNode* icon, StatsListType type, float x) { + slot = NewTabButton::create(TabBaseColor::Unselected, TabBaseColor::Selected, icon, this, menu_selector(MoreLeaderboards::onTab)); + slot->setPosition(x, 132); + slot->setTag(static_cast(type)); + slot->setZOrder(30); + slot->setScale(0.8f); + m_menu->addChild(slot); + }; + + switch (tab_page) { + case 0: + MoreLeaderboards::g_tab = StatsListType::Stars; + addTab(m_tab1, stars_sprite, StatsListType::Stars, -140.f); + addTab(m_tab2, moons_sprite, StatsListType::Moons, -86.f); + addTab(m_tab3, usercoins_sprite, StatsListType::UserCoins, -30.f); + addTab(m_tab4, demons_sprite, StatsListType::Demons, 26.f); + addTab(m_tab5, diamond_sprite, StatsListType::Diamonds, 82.f); + addTab(m_tab6, creators_sprite, StatsListType::Creators, 138.f); + break; + case 1: + if (geode::Loader::get()->isModLoaded("itzkiba.better_progression")) { + MoreLeaderboards::g_tab = StatsListType::BetterProgression; + addTab(m_tab1, better_progression, StatsListType::BetterProgression, -140.f); + } else { + MoreLeaderboards::g_tab = StatsListType::Pointercrate; + addTab(m_tab1, pointercrate, StatsListType::Pointercrate, -140.f); + } + addTab(m_tab2, makeClassicSpr("diffIcon_07_btn_001.png"), StatsListType::classicDemonsEasy, -86.f); + addTab(m_tab3, makeClassicSpr("diffIcon_08_btn_001.png"), StatsListType::classicDemonsMedium, -30.f); + addTab(m_tab4, makeClassicSpr("diffIcon_06_btn_001.png"), StatsListType::classicDemonsHard, 26.f); + addTab(m_tab5, makeClassicSpr("diffIcon_09_btn_001.png"), StatsListType::classicDemonsInsane, 82.f); + addTab(m_tab6, makeClassicSpr("diffIcon_10_btn_001.png"), StatsListType::classicDemonsExtreme, 138.f); + break; + case 2: + MoreLeaderboards::g_tab = StatsListType::platformerDemonsEasy; + addTab(m_tab1, makePlatformerSpr("diffIcon_07_btn_001.png"), StatsListType::platformerDemonsEasy, -140.f); + addTab(m_tab2, makePlatformerSpr("diffIcon_08_btn_001.png"), StatsListType::platformerDemonsMedium, -86.f); + addTab(m_tab3, makePlatformerSpr("diffIcon_06_btn_001.png"), StatsListType::platformerDemonsHard, -30.f); + addTab(m_tab4, makePlatformerSpr("diffIcon_09_btn_001.png"), StatsListType::platformerDemonsInsane, 26.f); + addTab(m_tab5, makePlatformerSpr("diffIcon_10_btn_001.png"), StatsListType::platformerDemonsExtreme, 82.f); + if (geode::Loader::get()->isModLoaded("itzkiba.better_progression")) + addTab(m_tab6, pointercrate, StatsListType::Pointercrate, 138.f); + break; + } + + this->addChild(m_menu); +} + +void MoreLeaderboards::onPageLeft(CCObject*) { + if (loading) return; + MoreLeaderboards::username = ""; + MoreLeaderboards::scroll_int = 0; + loading = true; + loading_circle = LoadingCircle::create(); + loading_circle->setParentLayer(this); + loading_circle->setZOrder(25); + loading_circle->show(); + if (displayedData) { displayedData->release(); displayedData = CCArray::create(); displayedData->retain(); } + page--; + resetInfos(); + if (loading) { startLoadingMore(); loadPageMore(); } +} + +void MoreLeaderboards::onPageRight(CCObject*) { + if (loading) return; + MoreLeaderboards::username = ""; + MoreLeaderboards::scroll_int = 0; + loading = true; + loading_circle = LoadingCircle::create(); + loading_circle->setZOrder(25); + loading_circle->setParentLayer(this); + loading_circle->show(); + if (displayedData) { displayedData->release(); displayedData = CCArray::create(); displayedData->retain(); } + page++; + resetInfos(); + if (loading) { startLoadingMore(); loadPageMore(); } +} + +void MoreLeaderboards::resetInfos() { + if (page_label != nullptr) { page_label->removeFromParentAndCleanup(true); page_label = nullptr; } + if (page_left != nullptr) { page_left->removeFromParentAndCleanup(true); page_left = nullptr; } + if (page_right != nullptr) { page_right->removeFromParentAndCleanup(true); page_right = nullptr; } + if (trophy != nullptr) { trophy->removeFromParentAndCleanup(true); trophy = nullptr; } + if (mod_filter_btn != nullptr) { mod_filter_btn->removeFromParentAndCleanup(true); mod_filter_btn = nullptr; } + if (mod_filter_type_btn != nullptr) { mod_filter_type_btn->removeFromParentAndCleanup(true); mod_filter_type_btn = nullptr; } +} + +void MoreLeaderboards::onTab(CCObject* pSender) { + if (loading) return; + loading = true; + + loading_circle = LoadingCircle::create(); + loading_circle->setZOrder(25); + loading_circle->setParentLayer(this); + loading_circle->show(); + + if (displayedData) { displayedData->release(); displayedData = CCArray::create(); displayedData->retain(); } + if (pSender) MoreLeaderboards::g_tab = static_cast(pSender->getTag()); + resetInfos(); + page = 0; + + auto toggleTab = [this](CCMenuItemToggler* member) { + auto isSelected = member->getTag() == static_cast(MoreLeaderboards::g_tab); + auto targetMenu = m_menu; + member->toggle(isSelected); + if (member->getParent() != targetMenu) { + member->retain(); + member->removeFromParent(); + targetMenu->addChild(member); + member->release(); + } + if (isSelected && m_tabsGradientStencil) + m_tabsGradientStencil->setPosition(member->m_onButton->convertToWorldSpace({0.f, 0.f})); + }; + + if (m_tab1) toggleTab(m_tab1); + if (m_tab2) toggleTab(m_tab2); + if (m_tab3) toggleTab(m_tab3); + if (m_tab4) toggleTab(m_tab4); + if (m_tab5) toggleTab(m_tab5); + if (m_tab6) toggleTab(m_tab6); + + if (loading) { startLoadingMore(); loadPageMore(); } +} diff --git a/src/ui/moreleaderboards/MoreLeaderboards.hpp b/src/ui/moreleaderboards/MoreLeaderboards.hpp new file mode 100644 index 0000000..284b869 --- /dev/null +++ b/src/ui/moreleaderboards/MoreLeaderboards.hpp @@ -0,0 +1,118 @@ +#pragma once +#include +#include +#include "mods/MoreLeaderboardsListView.hpp" +#include "region/SelectRegion.hpp" + +enum class StatsListType { + Stars, + Diamonds, + UserCoins, + Demons, + Moons, + Creators, + classicDemonsEasy, + classicDemonsMedium, + classicDemonsHard, + classicDemonsInsane, + classicDemonsExtreme, + platformerDemonsEasy, + platformerDemonsMedium, + platformerDemonsHard, + platformerDemonsInsane, + platformerDemonsExtreme, + BetterProgression, + Pointercrate +}; + +class MoreLeaderboards : public CCLayer { + MoreLeaderboardsListView* leaderboardView = nullptr; + CustomListView* leaderboardViewScore = nullptr; + GJListLayer* listLayer = nullptr; + LoadingCircle* loading_circle = nullptr; + cocos2d::CCArray* displayedData; + + CCLabelBMFont* page_label = nullptr; + CCMenuItemSpriteExtra* page_left = nullptr; + CCMenuItemSpriteExtra* page_right = nullptr; + CCMenuItemSpriteExtra* tab_page_left = nullptr; + CCMenuItemSpriteExtra* tab_page_right = nullptr; + CCMenuItemSpriteExtra* mod_filter_btn = nullptr; + CCMenuItemSpriteExtra* mod_filter_type_btn = nullptr; + CCSprite* trophy = nullptr; + + CCString* datas = nullptr; + std::string data_region = ""; + std::string country_id = "NAN"; + + CCMenu* m_menu; + CCMenuItemToggler* m_tab1; + CCMenuItemToggler* m_tab2; + CCMenuItemToggler* m_tab3; + CCMenuItemToggler* m_tab4; + CCMenuItemToggler* m_tab5; + CCMenuItemToggler* m_tab6; + int tab_page = 0; + + CCMenuItemSpriteExtra* m_search; + +#ifndef GEODE_IS_IOS + CCClippingNode* m_tabsGradientNode = nullptr; +#else + CCNode* m_tabsGradientNode = nullptr; +#endif + CCSprite* m_tabsGradientSprite = nullptr; + CCSprite* m_tabsGradientStencil = nullptr; + +protected: + virtual bool init(std::string type); + virtual void keyBackClicked(); + void startLoadingMods(); + void loadPageMods(); + void startLoadingMore(); + void loadPageMore(); + void loadPageStats(); + void resetInfos(); + +public: + static std::string data_response_moreLB; + static StatsListType g_tab; + static std::string username; + static int scroll_int; + static bool modFilter; + static int modFilterType; + bool loading = false; + + async::TaskHolder m_listener; + async::TaskHolder m_lbListener; + + void onMoreLeaderboards(CCObject*); + void onModsList(CCObject*); + void onRegion(CCObject*); + void onInfo(CCObject*); + void onLBInfo(CCObject*); + + void onTab(CCObject* pSender); + void onPageLeft(CCObject* pSender); + void onPageRight(CCObject* pSender); + + void loadTabPageButtons(); + void onTabPageLeft(CCObject* pSender); + void onTabPageRight(CCObject* pSender); + void onModFilter(CCObject* pSender); + void onModFilterType(CCObject* pSender); + void changeTabPage(); + + void onSearch(CCObject*); + + static MoreLeaderboards* create(std::string type); + void fadeLoadingCircle(); + void handle_request_mods(std::string const& data); + void handle_request_more(std::string const& data); + void startLoadingLeaderboard(); + + static std::vector getWords(std::string s, std::string delim); + static CCDictionary* responseToDict(const std::string& response); + + void backButton(CCObject*); +}; diff --git a/src/ui/moreleaderboards/mods/MoreLeaderboardsCell.cpp b/src/ui/moreleaderboards/mods/MoreLeaderboardsCell.cpp new file mode 100644 index 0000000..dd85b84 --- /dev/null +++ b/src/ui/moreleaderboards/mods/MoreLeaderboardsCell.cpp @@ -0,0 +1,134 @@ +#include "MoreLeaderboardsCell.hpp" +#include "../MoreLeaderboards.hpp" + +void MoreLeaderboardsCell::loadFromDict(cocos2d::CCDictionary* content) { + auto name = content->valueForKey("modstring")->getCString(); + + auto mediaMenu = CCMenu::create(); + float mediaXPosition = 327; + int mediaCount = 0; + std::string usernameTemp = ""; + + std::vector data = MoreLeaderboards::getWords(name, "?"); + std::vector data2 = data; + + while (data.size() > 0) { + std::string id = data[0]; + std::string val = data[1]; + + if (id == "1") { + usernameTemp = val; + } else if (id == "3") { + CCSprite* badge = nullptr; + if (val == "0") { + badge = cocos2d::CCSprite::createWithSpriteFrameName("robtop_badge.png"_spr); + } else if (val == "2") { + badge = cocos2d::CCSprite::createWithSpriteFrameName("modBadge_01_001.png"); + } else if (val == "1") { + badge = cocos2d::CCSprite::createWithSpriteFrameName("modBadge_02_001.png"); + } else if (val == "3") { + badge = cocos2d::CCSprite::createWithSpriteFrameName("modBadge_03_001.png"); + } + if (badge) { + badge->setPosition({30, 20}); + badge->setScale(1.225f); + this->m_mainLayer->addChild(badge); + } + } else if (id == "4") { + if (val != "NULL") { + auto discordBtn = CCMenuItemSpriteExtra::create( + CCSprite::createWithSpriteFrameName("gj_discordIcon_001.png"), + this, menu_selector(MoreLeaderboardsCell::callback_link) + ); + discordBtn->setUserObject(CCString::create(val)); + mediaMenu->addChild(discordBtn); + mediaCount++; + } + } else if (id == "5") { + if (val != "NULL") { + auto twitchBtn = CCMenuItemSpriteExtra::create( + CCSprite::createWithSpriteFrameName("gj_twitchIcon_001.png"), + this, menu_selector(MoreLeaderboardsCell::callback_link) + ); + twitchBtn->setUserObject(CCString::create(val)); + mediaMenu->addChild(twitchBtn); + mediaCount++; + } + } else if (id == "6") { + if (val != "NULL") { + auto youtubeBtn = CCMenuItemSpriteExtra::create( + CCSprite::createWithSpriteFrameName("gj_ytIcon_001.png"), + this, menu_selector(MoreLeaderboardsCell::callback_link) + ); + youtubeBtn->setUserObject(CCString::create(val)); + mediaMenu->addChild(youtubeBtn); + mediaCount++; + } + } + + data.erase(data.begin()); + data.erase(data.begin()); + } + + auto username = cocos2d::CCLabelBMFont::create(usernameTemp.c_str(), "goldFont.fnt"); + username->setPosition({57, 20}); + username->setScale(.725f); + + auto dictData = CCDictionary::create(); + dictData->setObject(CCString::create(data2[3]), "accountID"); + dictData->setObject(CCString::create(data2[13]), "playerID"); + dictData->setObject(CCString::create(usernameTemp.c_str()), "username"); + + auto usernameBtn = CCMenuItemSpriteExtra::create( + username, this, menu_selector(MoreLeaderboardsCell::callback_user) + ); + usernameBtn->setPosition({0, 0}); + usernameBtn->setAnchorPoint({0, 0.5}); + usernameBtn->setUserObject(dictData); + + auto menu = CCMenu::create(); + menu->addChild(usernameBtn); + menu->setPosition({55, 20}); + this->m_mainLayer->addChild(menu); + + if (mediaCount == 1) mediaXPosition = 327; + else if (mediaCount == 2) mediaXPosition = 308; + else if (mediaCount == 3) mediaXPosition = 289; + + mediaMenu->alignItemsHorizontallyWithPadding(5); + mediaMenu->setAnchorPoint({1, 0.5}); + mediaMenu->setPosition({mediaXPosition, 20}); + this->m_mainLayer->addChild(mediaMenu); + + this->m_mainLayer->setVisible(true); + this->m_backgroundLayer->setOpacity(255); +} + +MoreLeaderboardsCell::MoreLeaderboardsCell(const char* name, CCSize size) + : GenericListCell(name, size) {} + +MoreLeaderboardsCell* MoreLeaderboardsCell::create(const char* key, CCSize size) { + auto ret = new MoreLeaderboardsCell(key, size); + if (ret && ret->init()) { + ret->autorelease(); + } else { + delete ret; + ret = nullptr; + } + return ret; +} + +void MoreLeaderboardsCell::callback_user(CCObject* pSender) { + auto data = static_cast(static_cast(pSender)->getUserObject()); + auto accountID = numFromString(data->valueForKey("accountID")->getCString()).unwrapOrDefault(); + auto playerID = numFromString(data->valueForKey("playerID")->getCString()).unwrapOrDefault(); + auto username = data->valueForKey("username")->getCString(); + + GameLevelManager::sharedState()->storeUserName(playerID, accountID, username); + ProfilePage::create(accountID, true)->show(); +} + +void MoreLeaderboardsCell::callback_link(CCObject* pSender) { + auto data = static_cast(static_cast(pSender)->getUserObject()); + web::openLinkInBrowser(data->getCString()); +} diff --git a/src/ui/moreleaderboards/mods/MoreLeaderboardsCell.hpp b/src/ui/moreleaderboards/mods/MoreLeaderboardsCell.hpp new file mode 100644 index 0000000..4498132 --- /dev/null +++ b/src/ui/moreleaderboards/mods/MoreLeaderboardsCell.hpp @@ -0,0 +1,15 @@ +#pragma once +#include +#include + +class MoreLeaderboardsCell : public GenericListCell { +public: + inline static CCDictionary* static_content; + inline static CCString* static_data_other; + + MoreLeaderboardsCell(const char* name, CCSize size); + void loadFromDict(CCDictionary* dict); + void callback_user(CCObject* pSender); + void callback_link(CCObject* pSender); + static MoreLeaderboardsCell* create(const char* key, CCSize size); +}; diff --git a/src/ui/moreleaderboards/mods/MoreLeaderboardsListView.cpp b/src/ui/moreleaderboards/mods/MoreLeaderboardsListView.cpp new file mode 100644 index 0000000..05813db --- /dev/null +++ b/src/ui/moreleaderboards/mods/MoreLeaderboardsListView.cpp @@ -0,0 +1,65 @@ +#include "MoreLeaderboardsListView.hpp" +#include "MoreLeaderboardsCell.hpp" + +MoreLeaderboardsListView* MoreLeaderboardsListView::create(cocos2d::CCArray* levels, float width, float height) { + auto ret = new MoreLeaderboardsListView(); + if (ret && ret->init(levels, static_cast(0x420), width, height)) { + ret->autorelease(); + } else { + delete ret; + ret = nullptr; + } + return ret; +} + +void MoreLeaderboardsListView::setupList(float d) { + this->m_itemSeparation = 40.0f; + this->m_tableView->reloadData(); + + auto coverage = calculateNodeCoverage(m_tableView->m_contentLayer->getChildren()); + if (this->m_entries->count() > 4) + m_tableView->m_contentLayer->setContentSize({-coverage.origin.x + coverage.size.width, -coverage.origin.y + coverage.size.height}); + + this->m_tableView->moveToTop(); + + if (this->m_entries->count() == 1) + this->m_tableView->moveToTopWithOffset(this->m_itemSeparation); +} + +CCRect MoreLeaderboardsListView::calculateNodeCoverage(CCArray* nodes) { + CCRect coverage; + for (auto* childObj : CCArrayExt(nodes)) { + CCNode* child = childObj; + if (!child) continue; + // only process MoreLeaderboardsCell entries and plain CCNode spacers + if (!typeinfo_cast(child) && child->getChildrenCount() > 0) continue; + + auto contentSize = child->getScaledContentSize(); + contentSize.width *= child->getAnchorPoint().x; + contentSize.height *= child->getAnchorPoint().y; + auto pos = child->getPosition() - contentSize; + + contentSize = child->getScaledContentSize(); + auto contentPoint = CCPoint{1.f, 1.f} - child->getAnchorPoint(); + contentSize.width *= contentPoint.x; + contentSize.height *= contentPoint.y; + + auto csize = child->getPosition() + contentSize; + if (pos.x < coverage.origin.x) coverage.origin.x = pos.x; + if (pos.y < coverage.origin.y) coverage.origin.y = pos.y; + if (csize.x > coverage.size.width) coverage.size.width = csize.x; + if (csize.y > coverage.size.height) coverage.size.height = csize.y; + } + return coverage; +} + +void MoreLeaderboardsListView::loadCell(TableViewCell* cell, int index) { + static_cast(cell)->loadFromDict( + static_cast(this->m_entries->objectAtIndex(index)) + ); + static_cast(cell)->updateBGColor(index); +} + +TableViewCell* MoreLeaderboardsListView::getListCell(const char* key) { + return new MoreLeaderboardsCell(key, {this->m_width, 40.0f}); +} diff --git a/src/ui/moreleaderboards/mods/MoreLeaderboardsListView.hpp b/src/ui/moreleaderboards/mods/MoreLeaderboardsListView.hpp new file mode 100644 index 0000000..9f77127 --- /dev/null +++ b/src/ui/moreleaderboards/mods/MoreLeaderboardsListView.hpp @@ -0,0 +1,12 @@ +#pragma once +#include + +class MoreLeaderboardsListView : public CustomListView { +protected: + void setupList(float) override; + TableViewCell* getListCell(const char* key) override; + void loadCell(TableViewCell* cell, int index) override; + static CCRect calculateNodeCoverage(CCArray* nodes); +public: + static MoreLeaderboardsListView* create(CCArray* levels, float width, float height); +}; diff --git a/src/ui/moreleaderboards/region/SelectRegion.cpp b/src/ui/moreleaderboards/region/SelectRegion.cpp new file mode 100644 index 0000000..d273bfc --- /dev/null +++ b/src/ui/moreleaderboards/region/SelectRegion.cpp @@ -0,0 +1,66 @@ +#include "SelectRegion.hpp" + +std::vector SelectRegion::displayedData = {}; + +std::vector SelectRegion::getWords(std::string s, std::string delim) { + std::vector res; + std::string token = ""; + for (int i = 0; i < (int)s.size(); i++) { + bool flag = true; + for (int j = 0; j < (int)delim.size(); j++) { + if (s[i + j] != delim[j]) flag = false; + } + if (flag) { + if (token.size() > 0) { + res.push_back(token); + token = ""; + i += delim.size() - 1; + } + } else { + token += s[i]; + } + } + res.push_back(token); + return res; +} + +SelectRegion* SelectRegion::create(const std::function& callback) { + auto ret = new SelectRegion(callback); + if (ret && ret->init()) { + ret->autorelease(); + return ret; + } + CC_SAFE_DELETE(ret); + return nullptr; +} + +void SelectRegion::scene(const std::function& callback) { + if (auto popup = SelectRegion::create(callback)) { + CCDirector::sharedDirector()->getRunningScene()->addChild(popup); + } +} + +bool SelectRegion::init() { + if (!Popup::init(250.0f, 210.0f)) return false; + setTouchEnabled(true); + setKeypadEnabled(true); + loadPage(); + return true; +} + +void SelectRegion::loadPage() { + CCArray* data = CCArray::create(); + + for (std::string const& mod : SelectRegion::displayedData) { + auto cell = SelectRegionCell::create(mod, [this](std::string id) { + selectedRegion(id); + removeFromParent(); + }); + data->addObject(cell); + } + + auto border = OldBorder::create(ListView::create(data, 30, 218, 178), {0xBF, 0x72, 0x3E, 0xFF}, {220, 180}, {1, 1}); + border->setPosition(m_mainLayer->getContentSize() / 2 - border->getContentSize() / 2); + setTouchPriority(100); + m_mainLayer->addChild(border); +} diff --git a/src/ui/moreleaderboards/region/SelectRegion.hpp b/src/ui/moreleaderboards/region/SelectRegion.hpp new file mode 100644 index 0000000..45480f0 --- /dev/null +++ b/src/ui/moreleaderboards/region/SelectRegion.hpp @@ -0,0 +1,25 @@ +#pragma once +#include +#include +#include "SelectRegionCell.hpp" +#include "../../OldBorder.hpp" +#include + +class SelectRegion : public Popup { + LoadingCircle* loading_circle; + std::function selectedRegion; + +protected: + bool init(); + void loadPage(); + + cocos2d::CCSize m_sScrLayerSize; +public: + static std::vector displayedData; + + SelectRegion(const std::function& callback) : selectedRegion(callback) {} + + static SelectRegion* create(const std::function& callback); + static void scene(const std::function& callback); + static std::vector getWords(std::string s, std::string delim); +}; diff --git a/src/ui/moreleaderboards/region/SelectRegionCell.cpp b/src/ui/moreleaderboards/region/SelectRegionCell.cpp new file mode 100644 index 0000000..1612180 --- /dev/null +++ b/src/ui/moreleaderboards/region/SelectRegionCell.cpp @@ -0,0 +1,80 @@ +#include "SelectRegionCell.hpp" +#include "SelectRegion.hpp" + +std::string SelectRegionCell::country_id = "NAN"; + +bool SelectRegionCell::init(std::string mod) { + std::vector data = SelectRegion::getWords(mod, ":"); + std::vector data2 = data; + + while (data.size() > 0) { + std::string id = data[0]; + std::string name = data[1]; + + if (id == "1") { + auto username = cocos2d::CCLabelBMFont::create(name.c_str(), "bigFont.fnt"); + username->setPosition({38, 21}); + username->setAnchorPoint({0, 0.5}); + username->limitLabelWidth(100, .550f, .2f); + addChild(username); + } else if (id == "3") { + if (name != "NAN") { + std::string spriteName(name); + std::transform(spriteName.begin(), spriteName.end(), spriteName.begin(), ::tolower); + if (auto flagSpr = CCSprite::createWithSpriteFrameName((""_spr + spriteName + ".png").c_str())) { + flagSpr->setPosition({20, 15}); + flagSpr->setScaleY(30 / flagSpr->getContentHeight() * 0.50); + flagSpr->setScaleX(std::min(1.0f, 40 / flagSpr->getContentWidth())); + addChild(flagSpr); + } + } else { + auto earthSpr = CCSprite::create("earth_btn.png"_spr); + earthSpr->setScale(.425f); + earthSpr->setPosition({20, 14}); + addChild(earthSpr); + } + id_ctr = name; + } else if (id == "5") { + auto nbPlayers = cocos2d::CCLabelBMFont::create((name + " players").c_str(), "goldFont.fnt"); + nbPlayers->setPosition({38, 8}); + nbPlayers->setAnchorPoint({0, 0.5}); + nbPlayers->setScale(.375f); + addChild(nbPlayers); + } + + data.erase(data.begin()); + data.erase(data.begin()); + } + + CCMenu* btnMenu = CCMenu::create(); + btnMenu->setPosition(0, 0); + + const char* checkFrame = (id_ctr != country_id) + ? "GJ_checkOff_001.png" + : "GJ_checkOn_001.png"; + auto viewSpr = cocos2d::CCSprite::createWithSpriteFrameName(checkFrame); + viewSpr->setScale(.575f); + auto viewBtn = CCMenuItemSpriteExtra::create( + viewSpr, this, menu_selector(SelectRegionCell::onClick) + ); + viewBtn->setPosition(196, 15); + btnMenu->addChild(viewBtn); + addChild(btnMenu); + + return true; +} + +void SelectRegionCell::onClick(cocos2d::CCObject*) { + selectedRegion(country_id = id_ctr); +} + +SelectRegionCell* SelectRegionCell::create(std::string mod, const std::function& callback) { + auto ret = new SelectRegionCell(callback); + if (ret && ret->init(mod)) { + ret->retain(); + } else { + delete ret; + ret = nullptr; + } + return ret; +} diff --git a/src/ui/moreleaderboards/region/SelectRegionCell.hpp b/src/ui/moreleaderboards/region/SelectRegionCell.hpp new file mode 100644 index 0000000..e5a9f9b --- /dev/null +++ b/src/ui/moreleaderboards/region/SelectRegionCell.hpp @@ -0,0 +1,18 @@ +#pragma once +#include +#include + +class SelectRegion; + +class SelectRegionCell : public cocos2d::CCLayer { + std::function selectedRegion; + std::string id_ctr; + + void onClick(cocos2d::CCObject* pSender); +public: + SelectRegionCell(const std::function& callback) : selectedRegion(callback) {} + + static std::string country_id; + bool init(std::string mod); + static SelectRegionCell* create(std::string mod, const std::function& callback); +}; diff --git a/src/ui/moreleaderboards/tabs/TabButton.cpp b/src/ui/moreleaderboards/tabs/TabButton.cpp new file mode 100644 index 0000000..e897345 --- /dev/null +++ b/src/ui/moreleaderboards/tabs/TabButton.cpp @@ -0,0 +1,114 @@ +#include "TabButton.hpp" + +NewTabButton* NewTabButton::create( + TabBaseColor unselected, TabBaseColor selected, CCNode* icon, + cocos2d::CCObject* target, cocos2d::SEL_MenuHandler callback +) { + auto ret = new NewTabButton(); + auto sprOff = NewTabButtonSprite::create(icon, unselected); + auto sprOn = NewTabButtonSprite::create(icon, selected); + if (ret && ret->init(sprOff, sprOn, target, callback)) { + ret->m_offButton->m_colorDip = .3f; + ret->m_offButton->m_colorEnabled = true; + ret->m_offButton->m_scaleMultiplier = 1.f; + ret->m_onButton->setEnabled(false); + ret->autorelease(); + return ret; + } + CC_SAFE_DELETE(ret); + return nullptr; +} + +CCSize NewBasedButtonSprite::getMaxTopSize() const { + return m_obContentSize - CCSize(18.f, 18.f); +} + +void NewBasedButtonSprite::setTopOffset(CCPoint const& offset) { + m_topOffset = offset; + if (m_onTop) m_onTop->setPosition(this->getContentSize() / 2 + offset); +} + +void NewBasedButtonSprite::setTopRelativeScale(float scale) { + m_onTopRelativeScale = scale; + if (m_onTop) { + limitNodeSize(m_onTop, this->getMaxTopSize(), 999.f, .1f); + m_onTop->setScale(m_onTop->getScale() * m_onTopRelativeScale); + } +} + +bool NewBasedButtonSprite::initWithSprite( + char const* sprName, float sprScale, BaseType type, int size, int color +) { + auto spr = CCSprite::create(sprName); + if (!spr) return false; + m_onTopRelativeScale = sprScale; + return this->init(spr, type, size, color); +} + +bool NewBasedButtonSprite::initWithSpriteFrameName( + char const* sprName, float sprScale, BaseType type, int size, int color +) { + auto spr = CCSprite::createWithSpriteFrameName(sprName); + if (!spr) return false; + m_onTopRelativeScale = sprScale; + return this->init(spr, type, size, color); +} + +CCNode* NewBasedButtonSprite::getTopNode() const { + return m_onTop; +} + +NewBasedButtonSprite::~NewBasedButtonSprite() {} + +NewTabButtonSprite* NewTabButtonSprite::create(CCNode* icon, TabBaseColor color, TabBaseSize size) { + auto ret = new NewTabButtonSprite(); + if (ret && ret->init(icon, BaseType::Tab, static_cast(size), static_cast(color))) { + ret->autorelease(); + return ret; + } + CC_SAFE_DELETE(ret); + return nullptr; +} + +static std::string baseEnumsToString(BaseType type, int size, int color) { +#define ENUMS_TO_STRING(ty_) \ + case BaseType::ty_: { \ + sizeStr = baseEnumToString(static_cast(size)); \ + colorStr = baseEnumToString(static_cast(color)); \ + } break + + const char* typeStr = baseEnumToString(type); + const char* sizeStr; + const char* colorStr; + switch (type) { + ENUMS_TO_STRING(Circle); + ENUMS_TO_STRING(Cross); + ENUMS_TO_STRING(Account); + ENUMS_TO_STRING(IconSelect); + ENUMS_TO_STRING(Leaderboard); + ENUMS_TO_STRING(Editor); + ENUMS_TO_STRING(Tab); + ENUMS_TO_STRING(Category); + } + return fmt::format("newbase{}_{}_{}.png", typeStr, sizeStr, colorStr); +} + +bool NewBasedButtonSprite::init(CCNode* ontop, BaseType type, int size, int color) { + if (!CCSprite::initWithFile( + Mod::get()->expandSpriteName(baseEnumsToString(type, size, color)).data() + )) return false; + + m_type = type; + m_size = size; + m_color = color; + + if (ontop) { + m_onTop = ontop; + m_onTop->setPosition(this->getContentSize() / 2 + m_topOffset); + limitNodeSize(m_onTop, this->getMaxTopSize(), 999.f, .1f); + m_onTop->setScale(m_onTop->getScale() * m_onTopRelativeScale); + this->addChild(m_onTop); + } + + return true; +} diff --git a/src/ui/moreleaderboards/tabs/TabButton.hpp b/src/ui/moreleaderboards/tabs/TabButton.hpp new file mode 100644 index 0000000..5a5ad11 --- /dev/null +++ b/src/ui/moreleaderboards/tabs/TabButton.hpp @@ -0,0 +1,43 @@ +#pragma once +#include + +class NewTabButton : public CCMenuItemToggler { +public: + static NewTabButton* create( + TabBaseColor unselected, TabBaseColor selected, CCNode* icon, + cocos2d::CCObject* target, cocos2d::SEL_MenuHandler callback + ); +}; + +class NewBasedButtonSprite : public cocos2d::CCSprite { +protected: + BaseType m_type; + int m_size; + int m_color; + cocos2d::CCNode* m_onTop = nullptr; + float m_onTopRelativeScale = 1.3f; + cocos2d::CCPoint m_topOffset = cocos2d::CCPointZero; + + bool init(cocos2d::CCNode* ontop, BaseType type, int size, int color); + bool initWithSprite(const char* sprName, float sprScale, BaseType type, int size, int color); + bool initWithSpriteFrameName(const char* sprName, float sprScale, BaseType type, int size, int color); + + virtual cocos2d::CCSize getMaxTopSize() const; + virtual ~NewBasedButtonSprite(); + +public: + static NewBasedButtonSprite* create(cocos2d::CCNode* ontop, BaseType type, int size, int color); + + void setTopOffset(cocos2d::CCPoint const& offset); + void setTopRelativeScale(float scale); + cocos2d::CCNode* getTopNode() const; +}; + +class NewTabButtonSprite : public NewBasedButtonSprite { +public: + static NewTabButtonSprite* create( + CCNode* icon, + TabBaseColor color, + TabBaseSize size = TabBaseSize::Normal + ); +}; From 59680232adb1a988ef5d9f720ae82464b181a703 Mon Sep 17 00:00:00 2001 From: Jouca Date: Wed, 22 Apr 2026 14:00:35 +0200 Subject: [PATCH 16/32] add(rewrite): Add gdAwards2025Badge --- resources/badges/gdAwards2025Badge.png | Bin 0 -> 8371 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 resources/badges/gdAwards2025Badge.png diff --git a/resources/badges/gdAwards2025Badge.png b/resources/badges/gdAwards2025Badge.png new file mode 100644 index 0000000000000000000000000000000000000000..e47926f8c766da5ed0a514a3a09f81f72ed0d963 GIT binary patch literal 8371 zcmaKSRa9Ktwk-*6fgmBcg+hb7dvGUs(88ghDBRsiaED;QT>}XY1;K*{E8Mkkz3g+( z-tT?9{Z?yhO=+X|Vf|~)6{)830ULt^0|5a6TV76D<9RfGe$CKOp1-^$IAPBNF+@fe zq6xBuxSN832oe?`Ga!|`gQ*oz188dD={yJ&MnFIW*l6iObd{9^%|Q;VrhnP6dN?>e zqY)5<#XOu$&Fz2?Dl?#!jiU(7aa#usm5qf6jSi18yRwrM(Aq}M3k=lsQqeN^vNIR3 zpb-$CU5{kOsPB^>>XVMJw#~!!z=ha|7)9#hUz~i5IYf?{|!o4S&d2x1O`$G zuyVXNXJ_Z6;uBys<1^>wHaE8vV4>n<=j3K%=VRmGeb2!u$R!}i&Q0~-hvwNK*uqj! zLt6H~?w(g7G}aJ^lOP+LySqE9I~OYmY{kYQARxfT&dJ8f`Tp7By^E(K#MI-xqYLfd z4AMXsbFhsQ#0KO@^%tY58ORkPLh~H#|7PLf^bf70%YQ5NTr)NgQzteKR`$QL`rA-h z`TuX~;P8*N3q%9>f8zar61!-5Isw@Kt2AV=ZU@Z{H{_jGn zS%V-T7i*9c6(=t%I~9YDjiUv~-G%W#0A*!Cc}EwBsiQeiURs3anS#~E#zIhlS4M)L zpIw$qhMSXvLzaVIfS*@Nnq5Ltl7ov&T0-`3tTf2n)dA=T`5SBT50-=dU$KA5!NKX- zvosKF;|8>l1%n)@{*$wy&A-PY{jYrg#ajGpEbKD>ie-BahV8G;{$HK`d+S*~e@*|9 z?(^aw@dF*76%T%vHqThAF9HHNt-Q2^mdBq1UsNB%>EtVauLRogbIXO{X1+*~Zx|x(^c!IV@em;302|T$Rd4iAv#Sv2foO7?zp|J+~qu(nY;$1)4 zWtiYq|ID!9lAmVj%8%aAOTe+ZD_s77msV5<#^@M&Jp+gF)`C&d1fFl5iN+)5)xS zPUi4&wp_9W+Eb7}j8xI~i)I#zp2J=Y@^ZC%&x%>UZ@KNsXOK+blS=}hysQ=C^^=?i z=Vd-GJt}EcNr1h&AQH)R46<<&et^$-X!6jO*+rMn!QnosP_q1)>bsSkU171zjk|k9 zoQiTO*-|fAL@`UxO>B}^k$1eT0lIII2l?m|nW6zZf~pA>B;L-h>aW|u<{}!i+v8uQ z(@vFHLl^f#gKqaGzB}7{q#ffA$v?V$v-OjTm__-RTu0h^2P-hbVAdmp}?SIB~&@0m{Bmrh5cX`+&hV%tcD!&?5Ce0|d1 zcbGfen{;K7-S+!w8HNQ*j?0Yad)C`m(lql8c3#`*^sp;$j}I?TT77UMkXpdL#LIU3 z66>9vbAuUQ9-jJ?CVal3e}>~M{%paxA=F31m_~18)^7BWMHti1xIHUrM^?#%!C%AG z6{+JRoUY3D<~|E6CGco+=c*6o*AijE&uZLU(SH+J(Zwu+-%=rE1t0$zPt(DNgu2k5Z zn0O+tJX?zmXG}2f4g}!yfU^L*xVxb=k~_PBWMn^$p+5dez?+)h6rcWr9Vm`VPovJ{ zceRnoh2k+&rF(-PL*gbymDGKBnec!UBEv!?MT1Yx#)fTNTBfk!Q>+Y64_+El&3f26 z<}+D}ZNweRO`H=LV6vg&3pHMWOv)x?+Gu=pV}m+2ucfL!kw8@&oU3_Au)VzE3U;Uc zW4)`#)`R1f;s-Z6x3)#QOGe%(a(~KvmHEW{7`-6RrNF!`u2mYFF%+MbIpfvM=T__LD(D()t}=MQ7^}^Y>X!GtrOrh(oD_`FV{*xw{ab+$)iU zT&3iZSt`raZ97cnF9sEX){cVBzH75WPFFk~Jvhd5>9`&8AL`0bC#>Pr2e0XHH56d>l#(>1Uygc5$v=K{j7Z?BQ3&q%2wGM`!)Q%=3&LSk< zL9TCeG%%a{m1q=eDuU}9m$bUFjfddBlgkAvU3 zU+l+uv+mya6pJy7peOnSHh6uC`%*grcIF$25;VYA%6osL9!tfD5yMcvPqeaIez~2z zBMHO$#KUoPC8ceg_8E24rPr(9v`dV2(Gr(Y!bd_o+Gj&jGcvAoEpj|=kbQx8$9hC( zN2aznBcv&&6Mw)AXOrK}t3Dy-csPEzMg+BA(pr?c;hnttefAjvT7Ldbm4;^E9kpfG z!Jd|4_anRAUbqq@BSH$h$-jl|t-a@uwAADmz$k(}ZaOkcdN6$I`hJ)}=@oR&2`-;D za%-PYUd522?^juc5=uubSDyDu6QQqn9}egL`F6UmP{3X)VwE1faNi*AF;r7crpWYnL))AjOoKlnag!74ri(95NS>YD|DK)2780if%9t!QU%Cf%3jIoV6` zaNc_Ay&t*P^&>|?6V~B|blacU48MH7U0f_lQn!8y!18(He4phHOKH{ENVSyA%B0AC zeGU&|Zffuh)@E2kPhK+1Hxa-=2b1JO4KWH38;H5go8r z=LQ9n6o7FbF146rhwbU9E6!D=6d%ul#R}y>zcx2_S=LZhds>;Z%;2pz=c9w&*dv%9 zpF*(!TC$rLx2pv%;G(3D&2ME~8p$p;cx)K9T_<3v2|m&Ou-hV_TA_kzy`ZxfWvxmx zPEY_9Lv3MXvCSD$Ml~f_Ht&eoQf2^5rt+iz@DC*Z)8D=9C+Q`e1dA!_{X=DDe2X0+ zu%>mHz_F}~nB2x;Hl^?=VZVR?x4A{@rAf&^LQU@%AwdJcgI9IiRq-b#Nx!axO50zr zGz1=>_E+~3d}sG*FJ?1K`E3DCA_RsJp^^&W*Ggj%bA06nck(4CLq8p*=4m@g!0PAQ z!c^bSRxt!>$kdOdVNbrqYwHP)lR8{X&`RBwMrDZ&JK@aH$;Q7zrugiyuQcl9zjj-k z%v{!ed^$&Cxw}=zY3RGpbdB^vWfIyy8p1)@B>ScD>wSG2q+x9o+L~&l#K|J3dd2res4txLsx8g>@NE!^J7~Qa|zm(G8zoL)(Mq zCD649qkDF#wYxYMKN`hWj*Kks-!;$GbU;wgwyQT9Z1vP%xcQyaVvz{%9CA@hf!Ryq zHRq~2+&Er=T97ffHYE<|T7`Vl2tt&;R#H;_Xd~+Ip+8%;I&P(q1S+t9N%|7WXcWS& z%!TEpIK@NA2-Y1|v9Kzyt@;{}I}MC_Fi030OVu^kmi%!2J?GfoF!1b`VF~)Zw8O|Z zSETENovDKzd4iM`V)@X7YTsihjn*hFWntyl@A3IJZtXUEJR;Jj)Tp?8co*q4!udO!OsvPy0{1(w=BDMmes8TH>S0nG1Xm6cnXx#V5Sm9ou1uw z#h$4%pDcz;k5pC8+8 z`%Ajh5}?HyF*9M<%t9=4Qp;8on+f=M$lOK;GiR<2vl!KvTDb_gj81kO>6a<_u5WPV z0pa(b-vefYv*IZkP{r5=vuzju)tA*fSfP{;g0xwzIE;%N=d!AupYb>S=1RoS9ng^A zd=|~ARZOyuRRB>iFp=v+tmhD|kVIXAEx0xPbg;gMvfy%$vUAlX*Ebx1$$tT5k~?Dt z5Z=))xEHLhx*RhvYJG|;_G>3z`*DCqs;)qtYareEhmn_~i%X6pU^234Y++)B3(p<9 zVDOi=Py-+K+D#9%0ETvWr*AegN_nFi$rf?1raEk5mdj^>n$z9J{2BV7axvC8H0QY+~aDhma2Xsc$; zg7IbQCXn7piSp!*O}S-qzOyV>@9#^rJCtJJWIo^n!@ z@8l6Kb=2Cx@plY)EME>AizTSsU*mtceYUd@#U=_h+zEXNOys2@`)`&~0r z5*##CD}LcznZehKog0L}JEyV6;$gC(fA-8av-$`BN!+jxIu@FO$pnGF!MLM_=+FXf&;XDoG88B_r>b^dZ!cZ^Q z-Y3P&iH=LA!)3B4R3_N*L|*L1ObY@qz-rdN-T8$C=NUL@3{W0C#Ua6~seUJCh;nq| zT&y;;@ve7?!Lg2iO}9K*#RrTXgFCHwxNg-ZYk1WWwWt9d2$Xsuy7zuZCYAtY>Dg=NWPrKlpN+C)@1G<-rubeE|#%dF)EP zEac*Ks`7F_dwc;F^{XfJYyGw&bva%V{4PnLVoV7-)P@u878Bt}erf*Q4401NH@&v4 z8a7++REtfJ0qN^TD?UC89WW!(?^O z%No2D!Yy*z66Pwkb1>ewx<<=ZHEUo>J9P3ZUh_m|QoRX#Xm5l#xHfj)59QIMk+t8N<~ebdeWVT z@D7FcWAPq!VJ8Y$=Ira;&dY{`FfqGso!mxPGpa|l8bi0LqaIHEP~1@-wTf-3j@fEP zC_^cutefep-wig-TXYlOhT>IKoiW}}9Fy*A}!#)Qe zlMgL}x+lYyiXw*3)3Vg{!=2aS8Rv^$O{H|XpeOf35^GMa)kU9A&Tp8ubz<8(4o9-z zQgHUJu7bTxc>i#G<$@h*zOU81Wb+ZCRQSYsY5Ep%g+%b}t58&Om8nh3gtI}a*HA9X z^1fHtHqi+-aG6DG<4<}qK$rTHqa*y)f!bUzcsIvcDg|+sQIR~x{lQ&ZZStG=>}Jdo z^};n&V|_)1Bs{-zOBIroD!p!G&Gp^lSo@nu^Y;5T`>9Sd?DwWXvkB*6^%t!t((Xa&7lnxFkKz}i(YKk+Ey04tmqF`O_uJ?~s z*>U1tZ`g}%CCi8X@$W~`=6Fv()AM7*7!WgbBruPR=^MBr6>Q=Mkk!u=Tm^rpe+T%* z#)8n{_O(zqy}{P9ST@Y8X09{;$pl3_;Fo8SZmpj{t?_QlUGGOX{60EAnP zYb$6gzhc))9jw@XG-XtEW0{x|dmHI3(eds~kDR{f$9H{1e4f{FO;uEyEpo(EB<}3s zJCQd@atXXbIu%j9sFatN&KF5$-;q)s3`6&j8*!t0iKdF%9m#dLafKX-)b-2zym=``+Lzo4$=Y=mTv`PVr!I3L)}&R01U?1H%ogq+2&R85AQ~9yR1hAvsn)8cSUcF9 zP4`eAI@&qX1I(jHtCx-nO@2kTQ;0M*$}WLm=<13?p6x-Q9j*0&#~2f1PJ}%X(AYhe z_knD|{1Q&UX7!{rAynI_INwJqW_}`XJAkOqMQ}$awD^ySyHNh*n8u4H(T`}!BgsFU z-8{ZB=&T<)OlM#%zKs}s-Xr`s7ezThehN};0 z7MnJFSRy``#oJt~8vk{YTdt6PXulK%D^N9|B`q%e478j6GKqwQEA)tv4j`3@N#+*N zXps{d7mhtw7 zy)UA)JTx4tc2Q6xkI|jJ}|aS;q4**jKiw)if!{gU^D?NFLdkz!37! zT5Y?wogvxtH=~dAwgq0aVFUA#Nlb->B|$j7J^=uPgK&TNG8UPDZuC);*ZG(G zb7tGl9l1aAVMWj&hw5bTEdLTWrT@amz@b@bh8-dD;?r_^<24V@e7`|7h_fw;eF5Oy z?Sg)Q^?I;bz@!-AJTb84eiTk!DXL@c8O}90v(2^8>v&s9F?4q=;zs~1l*;?!VxUp` zi;J`HgX@D&gccJ&R#*$>(aHnkr<>}N>_U&D75HgSwOxFl_pU)LyBh9Z|clg0Mca#+f`70 zy?+g=8CEvxhm>hyk(^tf8A17+NKP>e{b5;BXCKwI`9J<4n>o}&zqYfu462lbAqSsx zDKI;ZkNr3y+RrpHEjU)mCnWQvWhiXpd>z>6Cpl(jRygVzYO z+g)+<*D0q-5%jifVF&*F$8b*rzEnfFGYKB$-GSegQG_&Jq0TT4$|qsGZzjOC%hEQH zftj@^#Od{O$@-S(qY^Txn~z+`AQ2ymrK8U>g8stYI4Gv^{RvW-no5+^Z>M~4L{#~Wa;WSeo7L$!W3C;rw#ZZdPV(#Qyy8f7eYybe=4w^B>h&ExJo}R9DkWy${n#F&@88~^&)y>)yh6<6>uqt+88~6O zVNl0-q9M){UG3pTnT__1;;su%G52G-Q4QcrNnOU4~2?4Cpom#Lti*u^`w!%vPPsSEZ0_J zSkDa@a0Dc_vM}h@AQ23q> z&0Nvfd8*vw6C%*ngS~*x?W}hj_0;Ocz2wUiXC$L!NAhaPrklTBs5ZE-t?!UC88J+F zv~SGEk9&_TuwLeU!wFSV8^266FH^Ilrf$nEi}Md~Ma3@hhVoiyjeMS(-mqEaZF;?5 zqw?xnl-W?Y8A`Ddn#BzPAS`d6f(^nxMj46jDz;GjiNZc4Ap6mDT1_Qt3#^cfn!J_G z&_gVe%&Eyg+!VW~c7D6#1ZK4H&-eZUC@EqfR4Xp0ena2X#Igsp+&&3l<@N7rxJi81 zzBc7?%k|owu4eItH?GFDUlm)5c7B6B>+eRJwD++hE)rs9y#t+SO-;Q0fIeLw<0ntn zF8is|Q=e^VVFPC*4tI_t(!KDdE!gjx`b~OUB^0jh;LCkfbLf>NUgzCtN=G_Y6aKs=Pq$!-d_2f)#n7LTJGIeZp3dm5rcS!y)`$Y#De=Z<%5VFpGx1Q<) z!pB0U7UiV$Kf0xqLn1t{cFo{?!(kxR--EZsq&^{J_}&9tI<}8D;$mP6jFNX}#FY;e z9Yo=`>%+GlbT9VgIO7j2>ayo9Gv}t@EcQ>K8x)4UJH?)Wz+7UmczQj7M@sYeD<5Kc zB<44)dx7JLr*A<`1fY($v1$GNb6o}U2**EgZ3PH3#N3Q7F@+9Ph^ARyEK?{%&=qhG zS6>(&5UI?t$)ZRKP@+F>^Y9A%#COgcyuUzr=wlBgUR0rpgq%Wx{ng1l!gxQr@|uci z3CzQcozD3XGq23%N3?>+^j#vzhcDf{@}V4rFk`iTP#8~&`%c0Rh4|CeVuRuiyFNen>x93Yqn6YhYXD;z! c2l874F{=8JQyh-2zkVZ-mr;?fk~9hW9|T(PzyJUM literal 0 HcmV?d00001 From f3eb477178379ff295c301e5d6f5edf7e26490f2 Mon Sep 17 00:00:00 2001 From: FireMario211 <17692105+FireMario211@users.noreply.github.com> Date: Wed, 22 Apr 2026 21:02:00 -0400 Subject: [PATCH 17/32] revert to step2 as the other doesnt appear to work --- src/modules/events/DailyChest.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/events/DailyChest.cpp b/src/modules/events/DailyChest.cpp index 063f1a5..2b7006f 100644 --- a/src/modules/events/DailyChest.cpp +++ b/src/modules/events/DailyChest.cpp @@ -123,7 +123,7 @@ void giveChestNotify(bool large) { // TODO: test RewardUnlockLayer::playRewardEffect to fix mod menus from skipping step2 #include class $modify(RULHook, RewardUnlockLayer) { - void playRewardEffect() { + void step2() { int tag = m_chestType; if (g_chestTx) { if (tag == 1) { @@ -139,7 +139,7 @@ class $modify(RULHook, RewardUnlockLayer) { } } refreshRewards(); - RewardUnlockLayer::playRewardEffect(); + RewardUnlockLayer::step2(); } }; From 936fb13f218a825fe86d8551a23d068236d57cf4 Mon Sep 17 00:00:00 2001 From: Jouca Date: Thu, 23 Apr 2026 17:20:25 +0200 Subject: [PATCH 18/32] fix(rewrite): Tentative of fixing chest bug --- src/modules/events/DailyChest.cpp | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/modules/events/DailyChest.cpp b/src/modules/events/DailyChest.cpp index 2b7006f..8ff689f 100644 --- a/src/modules/events/DailyChest.cpp +++ b/src/modules/events/DailyChest.cpp @@ -45,6 +45,7 @@ struct ChestMsg { static GJRewardItem* time1 = nullptr; static GJRewardItem* time2 = nullptr; static std::optional> g_chestTx; +static DailyChest* g_pendingRefresh = nullptr; void DailyChest::getRewards() { auto glm = GameLevelManager::sharedState(); @@ -57,10 +58,10 @@ void DailyChest::getRewards() { #include void DailyChest::rewardsStatusFinished(int p0) { GameStatsManager* gsm; - // Check if rewards are loaded gsm = GameStatsManager::sharedState(); if (gsm->m_rewardItems->count() == 0) { log::debug("[DailyChest] no reward items, aborting"); + if (g_pendingRefresh == this) { g_pendingRefresh = nullptr; this->release(); } return; } @@ -87,14 +88,21 @@ void DailyChest::rewardsStatusFinished(int p0) { }; (void)g_chestTx->trySend(std::move(data)); } + if (g_pendingRefresh == this) { g_pendingRefresh = nullptr; this->release(); } }; void DailyChest::rewardsStatusFailed() { log::error("[DailyChest] Failed to get rewards"); + if (g_pendingRefresh == this) { g_pendingRefresh = nullptr; this->release(); } }; static void refreshRewards() { + if (g_pendingRefresh) { + g_pendingRefresh->release(); + g_pendingRefresh = nullptr; + } auto dailyChest = new DailyChest(); + g_pendingRefresh = dailyChest; dailyChest->getRewards(); uint32_t t1 = 0; @@ -138,8 +146,14 @@ class $modify(RULHook, RewardUnlockLayer) { }); } } - refreshRewards(); RewardUnlockLayer::step2(); + // Delay the refresh so it doesn't race with the chest UI's own getGJRewards request. + // GD only allows one getGJRewards request at a time. + // A typical request takes 1-3s; 5s gives enough margin even on slow connections. + async::runtime().spawn([]() -> arc::Future<> { + co_await arc::sleepFor(Duration::fromSecs(5)); + Loader::get()->queueInMainThread(refreshRewards); + }); } }; From bf724c2adf9b806334cebc76f5bf7645bfc3733a Mon Sep 17 00:00:00 2001 From: Jouca Date: Thu, 23 Apr 2026 20:28:16 +0200 Subject: [PATCH 19/32] update(rewrite): Rewrite DailyChest --- src/modules/events/DailyChest.cpp | 88 ++++++++++++++----------------- 1 file changed, 39 insertions(+), 49 deletions(-) diff --git a/src/modules/events/DailyChest.cpp b/src/modules/events/DailyChest.cpp index 8ff689f..2ae72a8 100644 --- a/src/modules/events/DailyChest.cpp +++ b/src/modules/events/DailyChest.cpp @@ -9,6 +9,7 @@ using namespace asp::time; #else #include #endif + inline double rob_seconds(int remaining) { if (remaining == 0) return 0; #ifdef GEODE_IS_WINDOWS @@ -24,13 +25,12 @@ inline double rob_seconds(int remaining) { if (remaining < 0) return result; return remaining - result; } -inline double rob_seconds() { - return rob_seconds(-1); -} + enum class ChestKind { Small, Large }; + struct ChestMsg { enum class Type { Refresh, @@ -42,8 +42,7 @@ struct ChestMsg { uint32_t largeTime = 0; ChestKind opened{}; }; -static GJRewardItem* time1 = nullptr; -static GJRewardItem* time2 = nullptr; + static std::optional> g_chestTx; static DailyChest* g_pendingRefresh = nullptr; @@ -57,36 +56,26 @@ void DailyChest::getRewards() { #include void DailyChest::rewardsStatusFinished(int p0) { - GameStatsManager* gsm; - gsm = GameStatsManager::sharedState(); + auto gsm = GameStatsManager::sharedState(); if (gsm->m_rewardItems->count() == 0) { log::debug("[DailyChest] no reward items, aborting"); if (g_pendingRefresh == this) { g_pendingRefresh = nullptr; this->release(); } return; } - // Get the rewards - time1 = typeinfo_cast(gsm->m_rewardItems->objectForKey(0x1)); - time2 = typeinfo_cast(gsm->m_rewardItems->objectForKey(0x2)); - - uint32_t t1 = 0; - uint32_t t2 = 0; - if (time1) { - t1 = rob_seconds(time1->m_timeRemaining); - } - if (time2) { - t2 = rob_seconds(time2->m_timeRemaining); - } + auto item1 = typeinfo_cast(gsm->m_rewardItems->objectForKey(0x1)); + auto item2 = typeinfo_cast(gsm->m_rewardItems->objectForKey(0x2)); + uint32_t t1 = item1 ? (uint32_t)rob_seconds(item1->m_timeRemaining) : 0; + uint32_t t2 = item2 ? (uint32_t)rob_seconds(item2->m_timeRemaining) : 0; log::debug("[DailyChest] parsed timers: small={}s large={}s", t1, t2); if (g_chestTx) { - auto data = ChestMsg { + (void)g_chestTx->trySend(ChestMsg{ .type = ChestMsg::Type::Refresh, .smallTime = t1, .largeTime = t2 - }; - (void)g_chestTx->trySend(std::move(data)); + }); } if (g_pendingRefresh == this) { g_pendingRefresh = nullptr; this->release(); } }; @@ -104,14 +93,9 @@ static void refreshRewards() { auto dailyChest = new DailyChest(); g_pendingRefresh = dailyChest; dailyChest->getRewards(); - - uint32_t t1 = 0; - uint32_t t2 = 0; - if (time1) t1 = rob_seconds(time1->m_timeRemaining); - if (time2) t2 = rob_seconds(time2->m_timeRemaining); } -void giveChestNotify(bool large) { +static void giveChestNotify(bool large) { Loader::get()->queueInMainThread([large]() { EventData data; data.sprite = "GJ_square04.png"; @@ -164,14 +148,11 @@ static void toggleHook(bool value) { } } } -$on_game(Loaded) { - if (!Mod::get()->getSettingValue("largeChest") && !Mod::get()->getSettingValue("smallChest")) { - toggleHook(false); - return; - } + +static void startChestWatcher() { + if (g_chestTx) return; auto [tx, rx] = arc::mpsc::channel(); g_chestTx = std::move(tx); - // wait 5 seconds before async::runtime().spawn([rx = std::move(rx)]() mutable -> arc::Future<> { uint32_t smallRemaining = 0; uint32_t largeRemaining = 0; @@ -225,10 +206,6 @@ static void toggleHook(bool value) { switch (msg.type) { case ChestMsg::Type::Opened: log::debug("[DailyChest] rx -> chest opened: {}", msg.opened == ChestKind::Small ? "small" : "large"); - // smallRemaining = msg.smallTime; - // largeRemaining = msg.largeTime; - // smallAwait = smallRemaining > 0; - // largeAwait = largeRemaining > 0; if (msg.opened == ChestKind::Small) { smallAwait = false; smallNotified = false; @@ -277,21 +254,34 @@ static void toggleHook(bool value) { ); } }); + refreshRewards(); +} + +static void stopChestWatcher() { + if (!g_chestTx) return; + toggleHook(false); + (void)g_chestTx->trySend({ ChestMsg::Type::Stop }); + g_chestTx = std::nullopt; +} + +$on_game(Loaded) { + if (Mod::get()->getSettingValue("largeChest") || Mod::get()->getSettingValue("smallChest")) { + startChestWatcher(); + } else { + toggleHook(false); + } listenForSettingChanges("smallChest", [](bool value) { - if (!value && !Mod::get()->getSettingValue("largeChest")) { - if (g_chestTx) { - toggleHook(false); - (void)g_chestTx->trySend({ ChestMsg::Type::Stop }); - } + if (value) { + startChestWatcher(); + } else if (!Mod::get()->getSettingValue("largeChest")) { + stopChestWatcher(); } }); listenForSettingChanges("largeChest", [](bool value) { - if (!value && !Mod::get()->getSettingValue("smallChest")) { - if (g_chestTx) { - toggleHook(false); - (void)g_chestTx->trySend({ ChestMsg::Type::Stop }); - } + if (value) { + startChestWatcher(); + } else if (!Mod::get()->getSettingValue("smallChest")) { + stopChestWatcher(); } }); - refreshRewards(); } From 5116dfb7fd72e3c771305745772f342c70feb55b Mon Sep 17 00:00:00 2001 From: Jouca Date: Fri, 24 Apr 2026 01:34:44 +0200 Subject: [PATCH 20/32] update(rewrite): Rewrite gauntlets --- resources/gauntlets/particles.json | 1228 ++++----------------------- src/hooks/GauntletSelectLayer.cpp | 1234 ++++------------------------ 2 files changed, 294 insertions(+), 2168 deletions(-) diff --git a/resources/gauntlets/particles.json b/resources/gauntlets/particles.json index c179550..3bebcdb 100644 --- a/resources/gauntlets/particles.json +++ b/resources/gauntlets/particles.json @@ -1,1154 +1,228 @@ { "World": { - "background": { - "sprite": "world_gauntlet.png", - "scale": 1.27 - }, - "gauntlet": { - "position": [ - 1.0, - 16.0 - ], - "scale": 1.0 - }, - "particles": [ - { - "string": "22a-1a3a0a20a90a0a29a0a0a0a0a0a0a0a0a0a7a0a0a0a0.729412a0a0.352941a0a0.796078a0a1a0a7a0a0a0a0.729412a0a0.352941a0a0.796078a0a1a0a0.4a0a0.4a0a35a0a35a0a-50a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": [ - 10.0, - 24.0 - ], - "scale": [ - 1.31, - 2.61 - ] - }, - { - "string": "22a-1a3a0a20a90a0a29a0a0a0a0a0a0a0a0a0a7a0a0a0a0.34902a0a0.466667a0a0.792157a0a1a0a7a0a0a0a0.34902a0a0.466667a0a0.792157a0a1a0a0.4a0a0.4a0a35a0a35a0a-50a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": [ - 6.0, - 24.0 - ], - "scale": [ - 1.31, - 2.61 - ] - }, - { - "string": "22a-1a3a0a20a90a0a29a0a0a0a0a0a0a0a0a0a7a0a0a0a0.360784a0a0.854902a0a0.368627a0a1a0a7a0a0a0a0.360784a0a0.854902a0a0.368627a0a1a0a0.4a0a0.4a0a35a0a35a0a-50a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": [ - 2.0, - 24.0 - ], - "scale": [ - 1.31, - 2.61 - ] - }, - { - "string": "22a-1a3a0a20a90a0a29a0a0a0a0a0a0a0a0a0a7a0a0a0a0.866667a0a0.839216a0a0.247059a0a1a0a7a0a0a0a0.866667a0a0.839216a0a0.247059a0a1a0a0.4a0a0.4a0a35a0a35a0a-50a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": [ - -2.0, - 24.0 - ], - "scale": [ - 1.31, - 2.61 - ] - }, - { - "string": "22a-1a3a0a20a90a0a29a0a0a0a0a0a0a0a0a0a7a0a0a0a0.866667a0a0.294118a0a0.247059a0a1a0a7a0a0a0a0.866667a0a0.294118a0a0.247059a0a1a0a0.4a0a0.4a0a35a0a35a0a-50a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": [ - -6.0, - 24.0 - ], - "scale": [ - 1.31, - 2.61 - ] - }, - { - "string": "22a-1a3a0a20a90a0a29a0a0a0a0a0a0a0a0a0a7a0a0a0a0.729412a0a0.352941a0a0.796078a0a1a0a7a0a0a0a0.729412a0a0.352941a0a0.796078a0a1a0a0.4a0a0.4a0a35a0a35a0a-50a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": [ - -9.0, - 24.0 - ], - "scale": [ - -1.31, - 2.61 - ] - }, - { - "string": "22a-1a3a0a20a90a0a29a0a0a0a0a0a0a0a0a0a7a0a0a0a0.34902a0a0.466667a0a0.792157a0a1a0a7a0a0a0a0.34902a0a0.466667a0a0.792157a0a1a0a0.4a0a0.4a0a35a0a35a0a-50a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": [ - -5.0, - 24.0 - ], - "scale": [ - -1.31, - 2.61 - ] - }, - { - "string": "22a-1a3a0a20a90a0a29a0a0a0a0a0a0a0a0a0a7a0a0a0a0.360784a0a0.854902a0a0.368627a0a1a0a7a0a0a0a0.360784a0a0.854902a0a0.368627a0a1a0a0.4a0a0.4a0a35a0a35a0a-50a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": [ - -1.0, - 24.0 - ], - "scale": [ - -1.31, - 2.61 - ] - }, - { - "string": "22a-1a3a0a20a90a0a29a0a0a0a0a0a0a0a0a0a7a0a0a0a0.866667a0a0.839216a0a0.247059a0a1a0a7a0a0a0a0.866667a0a0.839216a0a0.247059a0a1a0a0.4a0a0.4a0a35a0a35a0a-50a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": [ - 3.0, - 24.0 - ], - "scale": [ - -1.31, - 2.61 - ] - }, - { - "string": "22a-1a3a0a20a90a0a29a0a0a0a0a0a0a0a0a0a7a0a0a0a0.866667a0a0.294118a0a0.247059a0a1a0a7a0a0a0a0.866667a0a0.294118a0a0.247059a0a1a0a0.4a0a0.4a0a35a0a35a0a-50a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": [ - 7.0, - 24.0 - ], - "scale": [ - -1.31, - 2.61 - ] - }, - { - "string": "38a-1a1.5a0.3a21a90a90a0a0a31a69a0a0a0a0a0a0a2a1a65a57a1a0a1a0a1a0a1a0a5a1a0a0a1a0a1a0a1a0a1a0a0.2a0a0.4a0a0a0a0a0a0a0a0a2a1a0a0a0a169a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": [ - 1.0, - 6.0 - ], - "scale": [ - 1.71, - 1.66 - ] - }, - { - "string": "11a-1a1.5a0.3a6a90a90a0a0a31a69a0a0a0a0a0a0a4a1a0a0a1a0a1a0a1a0a1a0a7a1a52a55a1a0a1a0a1a0a1a0a0.2a0a0.4a0a0a0a0a0a0a0a0a2a1a0a0a0a167a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": [ - 1.0, - 16.0 - ], - "scale": [ - 1.835, - 1.485 - ] - } + "background": { "sprite": "world_gauntlet.png", "scale": 1.27 }, + "gauntlet": { "position": [1.0, 16.0], "scale": 1.0, "zOrder": 3 }, + "particles": [ + { "string": "22a-1a3a0a20a90a0a29a0a0a0a0a0a0a0a0a0a7a0a0a0a0.729412a0a0.352941a0a0.796078a0a1a0a7a0a0a0a0.729412a0a0.352941a0a0.796078a0a1a0a0.4a0a0.4a0a35a0a35a0a-50a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": [10.0, 24.0], "scale": [1.31, 2.61] }, + { "string": "22a-1a3a0a20a90a0a29a0a0a0a0a0a0a0a0a0a7a0a0a0a0.34902a0a0.466667a0a0.792157a0a1a0a7a0a0a0a0.34902a0a0.466667a0a0.792157a0a1a0a0.4a0a0.4a0a35a0a35a0a-50a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": [6.0, 24.0], "scale": [1.31, 2.61] }, + { "string": "22a-1a3a0a20a90a0a29a0a0a0a0a0a0a0a0a0a7a0a0a0a0.360784a0a0.854902a0a0.368627a0a1a0a7a0a0a0a0.360784a0a0.854902a0a0.368627a0a1a0a0.4a0a0.4a0a35a0a35a0a-50a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": [2.0, 24.0], "scale": [1.31, 2.61] }, + { "string": "22a-1a3a0a20a90a0a29a0a0a0a0a0a0a0a0a0a7a0a0a0a0.866667a0a0.839216a0a0.247059a0a1a0a7a0a0a0a0.866667a0a0.839216a0a0.247059a0a1a0a0.4a0a0.4a0a35a0a35a0a-50a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": [-2.0, 24.0], "scale": [1.31, 2.61] }, + { "string": "22a-1a3a0a20a90a0a29a0a0a0a0a0a0a0a0a0a7a0a0a0a0.866667a0a0.294118a0a0.247059a0a1a0a7a0a0a0a0.866667a0a0.294118a0a0.247059a0a1a0a0.4a0a0.4a0a35a0a35a0a-50a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": [-6.0, 24.0], "scale": [1.31, 2.61] }, + { "string": "22a-1a3a0a20a90a0a29a0a0a0a0a0a0a0a0a0a7a0a0a0a0.729412a0a0.352941a0a0.796078a0a1a0a7a0a0a0a0.729412a0a0.352941a0a0.796078a0a1a0a0.4a0a0.4a0a35a0a35a0a-50a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": [-9.0, 24.0], "scale": [-1.31, 2.61] }, + { "string": "22a-1a3a0a20a90a0a29a0a0a0a0a0a0a0a0a0a7a0a0a0a0.34902a0a0.466667a0a0.792157a0a1a0a7a0a0a0a0.34902a0a0.466667a0a0.792157a0a1a0a0.4a0a0.4a0a35a0a35a0a-50a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": [-5.0, 24.0], "scale": [-1.31, 2.61] }, + { "string": "22a-1a3a0a20a90a0a29a0a0a0a0a0a0a0a0a0a7a0a0a0a0.360784a0a0.854902a0a0.368627a0a1a0a7a0a0a0a0.360784a0a0.854902a0a0.368627a0a1a0a0.4a0a0.4a0a35a0a35a0a-50a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": [-1.0, 24.0], "scale": [-1.31, 2.61] }, + { "string": "22a-1a3a0a20a90a0a29a0a0a0a0a0a0a0a0a0a7a0a0a0a0.866667a0a0.839216a0a0.247059a0a1a0a7a0a0a0a0.866667a0a0.839216a0a0.247059a0a1a0a0.4a0a0.4a0a35a0a35a0a-50a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": [3.0, 24.0], "scale": [-1.31, 2.61] }, + { "string": "22a-1a3a0a20a90a0a29a0a0a0a0a0a0a0a0a0a7a0a0a0a0.866667a0a0.294118a0a0.247059a0a1a0a7a0a0a0a0.866667a0a0.294118a0a0.247059a0a1a0a0.4a0a0.4a0a35a0a35a0a-50a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": [7.0, 24.0], "scale": [-1.31, 2.61] }, + { "string": "38a-1a1.5a0.3a21a90a90a0a0a31a69a0a0a0a0a0a0a2a1a65a57a1a0a1a0a1a0a1a0a5a1a0a0a1a0a1a0a1a0a1a0a0.2a0a0.4a0a0a0a0a0a0a0a0a2a1a0a0a0a169a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": [1.0, 6.0], "scale": [1.71, 1.66] }, + { "string": "11a-1a1.5a0.3a6a90a90a0a0a31a69a0a0a0a0a0a0a4a1a0a0a1a0a1a0a1a0a1a0a7a1a52a55a1a0a1a0a1a0a1a0a0.2a0a0.4a0a0a0a0a0a0a0a0a2a1a0a0a0a167a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": [1.0, 16.0], "scale": [1.835, 1.485] } ] }, "Fantasy": { - "background": { - "sprite": "fantasy_gauntlet.png", - "scale": 1.225 - }, - "gauntlet": { - "position": [ - -2.0, - 14.0 - ], - "scale": 1.0 - }, + "background": { "sprite": "fantasy_gauntlet.png", "scale": 1.225 }, + "gauntlet": { "position": [-2.0, 14.0], "scale": 0.925, "zOrder": 2 }, "particles": [ - { - "string": "26a-1a5a0.3a4a-90a14a9a0a17a0a0a0a0a0a0a0a2a1a76a325a0.537255a0a0.898039a0a0.164706a0a1a0a2a1a148a149a0.537255a0a0.898039a0a0.164706a0a1a0a0a0a0.3a0a0a0a0a0a0a0a0a2a0a0a0a0a146a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": null, - "scale": [ - 2.88, - 4.603 - ] - } + { "string": "26a-1a5a0.3a4a-90a14a9a0a17a0a0a0a0a0a0a0a2a1a76a325a0.537255a0a0.898039a0a0.164706a0a1a0a2a1a148a149a0.537255a0a0.898039a0a0.164706a0a1a0a0a0a0.3a0a0a0a0a0a0a0a0a2a0a0a0a0a146a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": [-2.0, 103.0], "scale": [2.88, 4.603] } ] }, "Portal": { - "background": { - "sprite": "portal_gauntlet.png", - "scale": 1.225 - }, - "gauntlet": { - "position": [ - -2.0, - 14.0 - ], - "scale": 1.0 - }, + "background": { "sprite": "portal_gauntlet.png", "scale": 1.225 }, + "gauntlet": { "position": [-2.0, 14.0], "scale": 0.925, "zOrder": 2 }, "particles": [ - { - "string": "13a-1a1a0.3a10a90a90a29a0a11a0a0a0a0a0a0a0a50a1a0a37a0.376471a0a0.109804a0a0.243137a0a1a0a32a1a142a96a0.376471a0a0.109804a0a0.243137a0a1a0a0.5a0a0.3a0a0a0a0a0a0a0a1a2a1a0a0a0a190a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": null, - "scale": [ - 2.88, - 3.978 - ] - }, - { - "string": "13a-1a1a0.3a10a90a90a29a0a11a0a0a0a0a0a0a0a50a1a0a37a0.0705882a0a0.172549a0a0.203922a0a1a0a32a1a142a96a0.0705882a0a0.172549a0a0.203922a0a1a0a0.5a0a0.3a0a0a0a0a0a0a0a1a2a1a0a0a0a190a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": null, - "scale": [ - 2.88, - 3.978 - ] - }, - { - "string": "38a-1a2a0.3a16a90a180a29a0a11a0a0a0a0a0a0a0a6a1a0a0a0.760784a0a0.380392a0a0.835294a0a1a0a3a1a0a0a0.760784a0a0.380392a0a0.835294a0a1a0a0.18a0a0.49a0a43a0a33a0a-34a0a1a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": null, - "scale": [ - 1.03, - 1.03 - ] - }, - { - "string": "38a-1a2a0.3a16a90a180a29a0a11a0a0a0a0a0a0a0a6a1a0a0a0.376471a0a0.690196a0a0.831373a0a1a0a3a1a0a0a0.376471a0a0.690196a0a0.831373a0a1a0a0.18a0a0.49a0a43a0a33a0a-34a0a1a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": null, - "scale": [ - 1.155, - 1.355 - ] - }, - { - "string": "30a-1a1a0.3a30a90a180a29a0a11a0a0a0a0a0a0a0a4a1a0a0a0.760784a0a0.380392a0a0.835294a0a1a0a2a1a0a0a0.760784a0a0.380392a0a0.835294a0a1a0a0.18a0a0.49a0a43a0a33a0a-41a0a1a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": null, - "scale": [ - 1.455, - 1.655 - ] - }, - { - "string": "30a-1a1a0.3a30a90a180a29a0a11a0a0a0a0a0a0a0a4a1a0a0a0.376471a0a0.690196a0a0.831373a0a1a0a2a1a0a0a0.376471a0a0.690196a0a0.831373a0a1a0a0.18a0a0.49a0a43a0a33a0a-41a0a1a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": null, - "scale": [ - 1.355, - 1.855 - ] - } + { "string": "13a-1a1a0.3a10a90a90a29a0a11a0a0a0a0a0a0a0a50a1a0a37a0.376471a0a0.109804a0a0.243137a0a1a0a32a1a142a96a0.376471a0a0.109804a0a0.243137a0a1a0a0.5a0a0.3a0a0a0a0a0a0a0a1a2a1a0a0a0a190a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": null, "scale": [2.88, 3.978] }, + { "string": "13a-1a1a0.3a10a90a90a29a0a11a0a0a0a0a0a0a0a50a1a0a37a0.0705882a0a0.172549a0a0.203922a0a1a0a32a1a142a96a0.0705882a0a0.172549a0a0.203922a0a1a0a0.5a0a0.3a0a0a0a0a0a0a0a1a2a1a0a0a0a190a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": null, "scale": [2.88, 3.978] }, + { "string": "38a-1a2a0.3a16a90a180a29a0a11a0a0a0a0a0a0a0a6a1a0a0a0.760784a0a0.380392a0a0.835294a0a1a0a3a1a0a0a0.760784a0a0.380392a0a0.835294a0a1a0a0.18a0a0.49a0a43a0a33a0a-34a0a1a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": null, "scale": [1.03, 1.03] }, + { "string": "38a-1a2a0.3a16a90a180a29a0a11a0a0a0a0a0a0a0a6a1a0a0a0.376471a0a0.690196a0a0.831373a0a1a0a3a1a0a0a0.376471a0a0.690196a0a0.831373a0a1a0a0.18a0a0.49a0a43a0a33a0a-34a0a1a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": null, "scale": [1.155, 1.355] }, + { "string": "30a-1a1a0.3a30a90a180a29a0a11a0a0a0a0a0a0a0a4a1a0a0a0.760784a0a0.380392a0a0.835294a0a1a0a2a1a0a0a0.760784a0a0.380392a0a0.835294a0a1a0a0.18a0a0.49a0a43a0a33a0a-41a0a1a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": null, "scale": [1.455, 1.655] }, + { "string": "30a-1a1a0.3a30a90a180a29a0a11a0a0a0a0a0a0a0a4a1a0a0a0.376471a0a0.690196a0a0.831373a0a1a0a2a1a0a0a0.376471a0a0.690196a0a0.831373a0a1a0a0.18a0a0.49a0a43a0a33a0a-41a0a1a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": null, "scale": [1.355, 1.855] } ] }, "Demon": { - "background": { - "sprite": "demon_gauntlet.png", - "scale": 1.275 - }, - "gauntlet": { - "position": [ - -1.0, - 22.0 - ], - "scale": 1.0 - }, + "background": { "sprite": "demon_gauntlet.png", "scale": 1.275 }, + "gauntlet": { "position": [-1.0, 22.0], "scale": 1.1, "zOrder": 2 }, "particles": [ - { - "string": "66a-1a3a0.3a20a90a27a11a0a37a38a0a0a0a0a0a0a5a1a0a0a0.694118a0a0.45098a0a0.054902a0a1a0a2a1a0a0a0.509804a0a0.172549a0a0a0a1a0a0.5a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": null, - "scale": [ - 1.0, - 1.0 - ] - }, - { - "string": "29a-1a3a0.3a8a90a27a11a0a37a38a0a0a0a0a0a0a50a1a0a0a0.0509804a0a0.0392157a0a0.0352941a0a1a0a50a1a0a0a0.0509804a0a0.0392157a0a0.0352941a0a1a0a0.5a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": null, - "scale": [ - 1.0, - 1.0 - ] - }, - { - "string": "31a-1a1a0.3a23a90a90a29a0a11a0a0a0a0a0a0a0a33a1a89a149a0.454902a0a0.321569a0a0.152941a0a1a0a50a1a145a68a0.454902a0a0.321569a0a0.152941a0a1a0a0.5a0a0.5a0a0a0a0a0a0a0a1a2a1a0a0a0a192a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": null, - "scale": [ - 1.0, - 1.0 - ] - }, - { - "string": "30a-1a2a0.3a13a90a8a21a0a8a0a0a45a0a0a0a0a40a1a89a149a0.396078a0a0.27451a0a0.14902a0a1a0a24a1a145a68a0.141176a0a0.0352941a0a0.0313726a0a1a0a0.5a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": null, - "scale": [ - 1.0, - 1.0 - ] - }, - { - "string": "30a-1a1a0.3a13a90a8a21a0a0a0a0a45a0a0a0a0a21a1a89a149a0.894118a0a0.631373a0a0.345098a0a1a0a6a1a145a68a0.894118a0a0.631373a0a0.345098a0a1a0a0.5a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a183a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": null, - "scale": [ - 1.0, - 1.0 - ] - } + { "string": "66a-1a3a0.3a20a90a27a11a0a37a38a0a0a0a0a0a0a5a1a0a0a0.694118a0a0.45098a0a0.054902a0a1a0a2a1a0a0a0.509804a0a0.172549a0a0a0a1a0a0.5a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": null, "scale": [1.0, 1.0] }, + { "string": "29a-1a3a0.3a8a90a27a11a0a37a38a0a0a0a0a0a0a50a1a0a0a0.0509804a0a0.0392157a0a0.0352941a0a1a0a50a1a0a0a0.0509804a0a0.0392157a0a0.0352941a0a1a0a0.5a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": null, "scale": [1.0, 1.0] }, + { "string": "31a-1a1a0.3a23a90a90a29a0a11a0a0a0a0a0a0a0a33a1a89a149a0.454902a0a0.321569a0a0.152941a0a1a0a50a1a145a68a0.454902a0a0.321569a0a0.152941a0a1a0a0.5a0a0.5a0a0a0a0a0a0a0a1a2a1a0a0a0a192a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": null, "scale": [1.0, 1.0] }, + { "string": "30a-1a2a0.3a13a90a8a21a0a8a0a0a45a0a0a0a0a40a1a89a149a0.396078a0a0.27451a0a0.14902a0a1a0a24a1a145a68a0.141176a0a0.0352941a0a0.0313726a0a1a0a0.5a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": null, "scale": [1.0, 1.0] }, + { "string": "30a-1a1a0.3a13a90a8a21a0a0a0a0a45a0a0a0a0a21a1a89a149a0.894118a0a0.631373a0a0.345098a0a1a0a6a1a145a68a0.894118a0a0.631373a0a0.345098a0a1a0a0.5a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a183a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": null, "scale": [1.0, 1.0] } ] }, "Castle": { - "background": { - "sprite": "castle_gauntlet.png", - "scale": 1.275 - } + "background": { "sprite": "castle_gauntlet.png", "scale": 1.275 } }, "Galaxy": { - "background": { - "sprite": "galaxy_gauntlet.png", - "scale": 1.695 - }, - "gauntlet": { - "position": [ - 0.0, - 11.0 - ], - "scale": 0.975 - }, + "background": { "sprite": "galaxy_gauntlet.png", "scale": 1.695 }, + "gauntlet": { "position": [0.0, 11.0], "scale": 0.975, "zOrder": 3 }, "particles": [ - { - "string": "11a-1a1a0.3a8a90a90a0a0a0a0a0a0a0a0a0a0a50a3a0a0a0.627451a0a0.286275a0a0.470588a0a1a0a50a3a106a70a0.627451a0a0.286275a0a0.470588a0a1a0a0a0a0a0a0a0a0a0a0a0a0a2a1a0a0a0a176a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": [ - 0.0, - 24.0 - ], - "scale": [ - 1.61, - 1.61 - ] - }, - { - "string": "14a-1a1.2a0.3a9a112a6a42a0a15a0a0a0a0a0a0a0a4a0a340a0a0.913725a0a0.72549a0a0.337255a0a1a0a4a0a340a0a0.913725a0a0.72549a0a0.337255a0a1a0a0.3a0a0.3a0a0a0a0a0a0a0a0a2a1a0a0a0a31a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": [ - -25.0, - 111.0 - ], - "scale": [ - 2.66, - 3.71 - ] - }, - { - "string": "38a-1a1.5a0.3a21a90a90a0a0a31a69a0a0a0a0a0a0a2a1a65a57a0.886275a0a0.866667a0a0.454902a0a1a0a5a1a0a0a0.886275a0a0.866667a0a0.454902a0a1a0a0.2a0a0.4a0a0a0a0a0a0a0a0a2a1a0a0a0a169a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": [ - 2.0, - 6.0 - ], - "scale": [ - 1.685, - 1.485 - ] - }, - { - "string": "11a-1a1.5a0.3a6a90a90a0a0a31a69a0a0a0a0a0a0a4a1a0a0a0.890196a0a0.376471a0a0.65098a0a1a0a7a1a52a55a0.890196a0a0.376471a0a0.65098a0a1a0a0.2a0a0.4a0a0a0a0a0a0a0a0a2a1a0a0a0a167a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": [ - 2.0, - -2.0 - ], - "scale": [ - 1.31, - 1.635 - ] - }, - { - "string": "4a-1a1a0.3a3a90a90a0a0a0a0a0a0a0a0a0a0a50a3a0a0a0.862745a0a0.352941a0a0.623529a0a1a0a50a3a106a22a0.862745a0a0.352941a0a0.623529a0a1a0a0.3a0a0.3a0a0a0a0a0a0a0a0a2a1a0a0a0a164a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": [ - 0.0, - 24.0 - ], - "scale": [ - 1.785, - 1.785 - ] - } + { "string": "11a-1a1a0.3a8a90a90a0a0a0a0a0a0a0a0a0a0a50a3a0a0a0.627451a0a0.286275a0a0.470588a0a1a0a50a3a106a70a0.627451a0a0.286275a0a0.470588a0a1a0a0a0a0a0a0a0a0a0a0a0a0a2a1a0a0a0a176a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": [0.0, 24.0], "scale": [1.61, 1.61] }, + { "string": "14a-1a1.2a0.3a9a112a6a42a0a15a0a0a0a0a0a0a0a4a0a340a0a0.913725a0a0.72549a0a0.337255a0a1a0a4a0a340a0a0.913725a0a0.72549a0a0.337255a0a1a0a0.3a0a0.3a0a0a0a0a0a0a0a0a2a1a0a0a0a31a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": [-25.0, 111.0], "scale": [2.66, 3.71], "rotation": 180 }, + { "string": "38a-1a1.5a0.3a21a90a90a0a0a31a69a0a0a0a0a0a0a2a1a65a57a0.886275a0a0.866667a0a0.454902a0a1a0a5a1a0a0a0.886275a0a0.866667a0a0.454902a0a1a0a0.2a0a0.4a0a0a0a0a0a0a0a0a2a1a0a0a0a169a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": [2.0, 6.0], "scale": [1.685, 1.485] }, + { "string": "11a-1a1.5a0.3a6a90a90a0a0a31a69a0a0a0a0a0a0a4a1a0a0a0.890196a0a0.376471a0a0.65098a0a1a0a7a1a52a55a0.890196a0a0.376471a0a0.65098a0a1a0a0.2a0a0.4a0a0a0a0a0a0a0a0a2a1a0a0a0a167a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": [2.0, -2.0], "scale": [1.31, 1.635] }, + { "string": "4a-1a1a0.3a3a90a90a0a0a0a0a0a0a0a0a0a0a50a3a0a0a0.862745a0a0.352941a0a0.623529a0a1a0a50a3a106a22a0.862745a0a0.352941a0a0.623529a0a1a0a0.3a0a0.3a0a0a0a0a0a0a0a0a2a1a0a0a0a164a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": [0.0, 24.0], "scale": [1.785, 1.785] } ] }, "Universe": { - "background": { - "sprite": "universe_gauntlet.png", - "scale": 1.27 - }, - "gauntlet": { - "position": [ - 0.0, - 16.0 - ], - "scale": 1.0 - }, + "background": { "sprite": "universe_gauntlet.png", "scale": 1.27 }, + "gauntlet": { "position": [0.0, 16.0], "scale": 0.975, "zOrder": 3 }, "particles": [ - { - "string": "23a-1a3a0.3a6a90a180a29a0a11a0a0a0a0a0a0a0a38a1a0a0a0.0627451a0a0.0980392a0a0.105882a0a1a0a50a1a0a0a0.0627451a0a0.0980392a0a0.105882a0a1a0a0.1a0a0.5a0a10a0a28a0a0a0a1a2a1a0a1a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": [ - 0.0, - 11.0 - ], - "scale": [ - 1.485, - 2.685 - ] - }, - { - "string": "20a-1a0.5a0a15a90a0a44a0a11a0a0a0a0a0a0a0a4a1a0a0a0.760784a0a0.447059a0a0.94902a0a1a0a4a1a0a0a0.760784a0a0.447059a0a0.94902a0a1a0a0.1a0a0.1a0a0a0a0a0a0a0a0a2a1a0a0a0a33a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": [ - 32.0, - -74.0 - ], - "scale": [ - 3.685, - 8.31 - ] - }, - { - "string": "19a-1a0.5a0a38a90a0a44a0a20a0a0a0a0a0a0a0a2a1a0a0a0.94902a0a0.67451a0a0.447059a0a1a0a2a1a0a0a0.94902a0a0.67451a0a0.447059a0a1a0a0.1a0a0.1a0a0a0a0a0a0a0a0a2a1a0a0a0a33a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": [ - 34.0, - -96.0 - ], - "scale": [ - 2.135, - 8.935 - ] - }, - { - "string": "20a-1a1a0a15a90a0a36a0a11a0a0a0a0a0a0a0a4a1a0a0a0.447059a0a0.654902a0a0.94902a0a1a0a4a1a0a0a0.447059a0a0.654902a0a0.94902a0a1a0a0.2a0a0.2a0a0a0a0a0a0a0a0a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": [ - 41.0, - -92.0 - ], - "scale": [ - 3.235, - 6.585 - ] - }, - { - "string": "11a-1a1a0.3a8a90a90a0a0a0a0a0a0a0a0a0a0a50a3a0a0a0.239216a0a0.403922a0a0.419608a0a1a0a50a3a106a70a0.239216a0a0.403922a0a0.419608a0a1a0a0a0a0a0a0a0a0a0a0a0a0a2a1a0a0a0a176a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": [ - 0.0, - 24.0 - ], - "scale": [ - 1.71, - 1.71 - ] - }, - { - "string": "4a-1a1a0.3a3a90a90a0a0a0a0a0a0a0a0a0a0a50a3a0a0a0.32549a0a0.537255a0a0.560784a0a1a0a50a3a106a22a0.32549a0a0.537255a0a0.560784a0a1a0a0.3a0a0.3a0a0a0a0a0a0a0a0a2a1a0a0a0a190a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": [ - -2.0, - 24.0 - ], - "scale": [ - 2.71, - 2.71 - ] - } + { "string": "23a-1a3a0.3a6a90a180a29a0a11a0a0a0a0a0a0a0a38a1a0a0a0.0627451a0a0.0980392a0a0.105882a0a1a0a50a1a0a0a0.0627451a0a0.0980392a0a0.105882a0a1a0a0.1a0a0.5a0a10a0a28a0a0a0a1a2a1a0a1a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": [0.0, 11.0], "scale": [1.485, 2.685] }, + { "string": "20a-1a0.5a0a15a90a0a44a0a11a0a0a0a0a0a0a0a4a1a0a0a0.760784a0a0.447059a0a0.94902a0a1a0a4a1a0a0a0.760784a0a0.447059a0a0.94902a0a1a0a0.1a0a0.1a0a0a0a0a0a0a0a0a2a1a0a0a0a33a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": [32.0, -74.0], "scale": [3.685, 8.31], "rotation": -20 }, + { "string": "19a-1a0.5a0a38a90a0a44a0a20a0a0a0a0a0a0a0a2a1a0a0a0.94902a0a0.67451a0a0.447059a0a1a0a2a1a0a0a0.94902a0a0.67451a0a0.447059a0a1a0a0.1a0a0.1a0a0a0a0a0a0a0a0a2a1a0a0a0a33a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": [34.0, -96.0], "scale": [2.135, 8.935], "rotation": -20 }, + { "string": "20a-1a1a0a15a90a0a36a0a11a0a0a0a0a0a0a0a4a1a0a0a0.447059a0a0.654902a0a0.94902a0a1a0a4a1a0a0a0.447059a0a0.654902a0a0.94902a0a1a0a0.2a0a0.2a0a0a0a0a0a0a0a0a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": [41.0, -92.0], "scale": [3.235, 6.585], "rotation": -20 }, + { "string": "11a-1a1a0.3a8a90a90a0a0a0a0a0a0a0a0a0a0a50a3a0a0a0.239216a0a0.403922a0a0.419608a0a1a0a50a3a106a70a0.239216a0a0.403922a0a0.419608a0a1a0a0a0a0a0a0a0a0a0a0a0a0a2a1a0a0a0a176a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": [0.0, 24.0], "scale": [1.71, 1.71] }, + { "string": "4a-1a1a0.3a3a90a90a0a0a0a0a0a0a0a0a0a0a50a3a0a0a0.32549a0a0.537255a0a0.560784a0a1a0a50a3a106a22a0.32549a0a0.537255a0a0.560784a0a1a0a0.3a0a0.3a0a0a0a0a0a0a0a0a2a1a0a0a0a190a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": [-2.0, 24.0], "scale": [2.71, 2.71] } ] }, "Cursed": { - "background": { - "sprite": "cursed_gauntlet.png", - "scale": 1.27 - }, - "gauntlet": { - "position": [ - 1.0, - 16.0 - ], - "scale": 1.0 - }, + "background": { "sprite": "cursed_gauntlet.png", "scale": 1.27 }, + "gauntlet": { "position": [1.0, 16.0], "scale": 1.0, "zOrder": 3 }, "particles": [ - { - "string": "5a-1a1a0.3a3a90a90a29a0a11a0a0a0a0a0a0a0a50a1a24a45a0.105882a0a0.380392a0a0.105882a0a1a0a44a1a48a28a0.105882a0a0.380392a0a0.105882a0a1a0a0a0a0a0a0a0a0a0a0a0a1a2a1a0a0a0a175a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": null, - "scale": [ - 2.035, - 2.035 - ] - }, - { - "string": "5a-1a1a0.3a3a90a90a29a0a11a0a0a0a0a0a0a0a50a1a24a45a0.105882a0a0.380392a0a0.105882a0a1a0a44a1a48a28a0.105882a0a0.380392a0a0.105882a0a1a0a0a0a0a0a0a0a0a0a0a0a1a2a1a0a0a0a175a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": null, - "scale": [ - 2.035, - 2.035 - ] - }, - { - "string": "93a-1a1a0.3a71a90a0a52a0a10a0a23a0a0a0a0a0a2a1a0a0a0.00392157a0a0.905882a0a0a0a1a0a2a1a30a0a0.00392157a0a0.905882a0a0a0a1a0a0.3a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a32a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": [ - 17.0, - 94.0 - ], - "scale": [ - 4.51, - 3.335 - ] - } + { "string": "5a-1a1a0.3a3a90a90a29a0a11a0a0a0a0a0a0a0a50a1a24a45a0.105882a0a0.380392a0a0.105882a0a1a0a44a1a48a28a0.105882a0a0.380392a0a0.105882a0a1a0a0a0a0a0a0a0a0a0a0a0a1a2a1a0a0a0a175a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": null, "scale": [2.035, 2.035] }, + { "string": "5a-1a1a0.3a3a90a90a29a0a11a0a0a0a0a0a0a0a50a1a24a45a0.105882a0a0.380392a0a0.105882a0a1a0a44a1a48a28a0.105882a0a0.380392a0a0.105882a0a1a0a0a0a0a0a0a0a0a0a0a0a1a2a1a0a0a0a175a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": null, "scale": [2.035, 2.035] }, + { "string": "93a-1a1a0.3a71a90a0a52a0a10a0a23a0a0a0a0a0a2a1a0a0a0.00392157a0a0.905882a0a0a0a1a0a2a1a30a0a0.00392157a0a0.905882a0a0a0a1a0a0.3a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a32a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": [17.0, 94.0], "scale": [4.51, 3.335], "rotation": 180 } ] }, "Doom": { - "background": { - "sprite": "doom_gauntlet.png", - "scale": 1.27 - }, - "gauntlet": { - "position": [ - 0.0, - 19.0 - ], - "scale": 1.0 - }, + "background": { "sprite": "doom_gauntlet.png", "scale": 1.27 }, + "gauntlet": { "position": [0.0, 19.0], "scale": 1.025, "zOrder": 3 }, "particles": [ - { - "string": "66a-1a3a0.3a20a90a27a11a0a43a83a0a0a0a0a0a0a5a1a0a0a0.733333a0a0.607843a0a0.12549a0a1a0a3a1a0a0a0.733333a0a0.470588a0a0.12549a0a1a0a0.5a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": [ - 0.0, - -7.0 - ], - "scale": [ - 1.235, - 1.235 - ] - }, - { - "string": "44a-1a3a0.3a13a90a27a11a0a32a62a0a0a0a0a0a0a50a1a0a0a0.0666667a0a0.0509804a0a0.0470588a0a1a0a50a1a0a0a0.0666667a0a0.0509804a0a0.0470588a0a1a0a0.5a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": [ - 0.0, - -13.0 - ], - "scale": [ - 1.31, - 1.435 - ] - }, - { - "string": "6a-1a1.9a0a3a90a17a69a0a3a0a0a-102a0a0a0a0a4a1a57a105a1a0a1a0a1a0a1a0a4a1a0a0a1a0a1a0a1a0a1a0a0a0a0a0a0a0a0a0a0a0a0a2a1a0a0a0a145a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": null, - "scale": [ - 2.035, - 2.035 - ] - } + { "string": "66a-1a3a0.3a20a90a27a11a0a43a83a0a0a0a0a0a0a5a1a0a0a0.733333a0a0.607843a0a0.12549a0a1a0a3a1a0a0a0.733333a0a0.470588a0a0.12549a0a1a0a0.5a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": [0.0, -7.0], "scale": [1.235, 1.235] }, + { "string": "44a-1a3a0.3a13a90a27a11a0a32a62a0a0a0a0a0a0a50a1a0a0a0.0666667a0a0.0509804a0a0.0470588a0a1a0a50a1a0a0a0.0666667a0a0.0509804a0a0.0470588a0a1a0a0.5a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": [0.0, -13.0], "scale": [1.31, 1.435] }, + { "string": "6a-1a1.9a0a3a90a17a69a0a3a0a0a-102a0a0a0a0a4a1a57a105a1a0a1a0a1a0a1a0a4a1a0a0a1a0a1a0a1a0a1a0a0a0a0a0a0a0a0a0a0a0a0a2a1a0a0a0a145a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": null, "scale": [2.035, 2.035] } ] }, "Split": { - "background": { - "sprite": "split_gauntlet.png", - "scale": 1.27 - }, - "gauntlet": { - "position": [ - -1.0, - 20.0 - ], - "scale": 1.0 - }, + "background": { "sprite": "split_gauntlet.png", "scale": 1.27 }, + "gauntlet": { "position": [-1.0, 20.0], "scale": 0.975, "zOrder": 2 }, "particles": [ - { - "string": "48a-1a2a0.3a20a90a2a19a0a10a20a0a0a0a0a0a0a4a1a0a0a0.666667a0a0.360784a0a0.603922a0a1a0a2a1a0a0a0.666667a0a0.360784a0a0.603922a0a1a0a0.5a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a31a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": [ - -28.0, - -65.0 - ], - "scale": [ - 2.18, - 4.003 - ] - }, - { - "string": "48a-1a2a0.3a20a90a2a19a0a10a20a0a0a0a0a0a0a4a1a0a0a0.454902a0a0.215686a0a0.403922a0a1a0a2a1a0a0a0.454902a0a0.215686a0a0.403922a0a1a0a0.5a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": [ - -28.0, - -65.0 - ], - "scale": [ - 2.18, - 4.003 - ] - }, - { - "string": "48a-1a2a0.3a20a90a2a19a0a10a20a0a0a0a0a0a0a4a1a0a0a0.243137a0a0.329412a0a0.341176a0a1a0a2a1a0a0a0.243137a0a0.329412a0a0.341176a0a1a0a0.5a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a31a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": [ - 25.0, - 65.0 - ], - "scale": [ - 2.18, - 4.003 - ] - }, - { - "string": "48a-1a2a0.3a20a90a2a19a0a10a20a0a0a0a0a0a0a4a1a0a0a0.145098a0a0.203922a0a0.207843a0a1a0a2a1a0a0a0.145098a0a0.203922a0a0.207843a0a1a0a0.5a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": [ - 25.0, - 65.0 - ], - "scale": [ - 2.18, - 4.003 - ] - }, - { - "string": "30a-1a2a0.3a13a90a90a29a0a11a0a0a0a0a0a0a0a6a1a0a0a0.705882a0a0.34902a0a0.698039a0a1a0a2a1a0a0a0.584314a0a0.34902a0a0.705882a0a1a0a0a0a0.5a0a6a0a34a0a0a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": [ - 0.0, - 29.0 - ], - "scale": [ - 1.955, - 1.955 - ] - }, - { - "string": "30a-1a2a0.3a13a90a90a29a0a11a0a0a0a0a0a0a0a6a1a0a0a0.254902a0a0.333333a0a0.388235a0a1a0a2a1a0a0a0.254902a0a0.333333a0a0.388235a0a1a0a0a0a0.5a0a6a0a34a0a0a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": [ - 0.0, - 29.0 - ], - "scale": [ - 1.955, - 1.955 - ] - }, - { - "string": "30a-1a2a0.3a13a90a90a29a0a11a0a0a0a0a0a0a0a6a1a0a0a0.705882a0a0.34902a0a0.698039a0a1a0a2a1a0a0a0.584314a0a0.34902a0a0.705882a0a1a0a0a0a0.5a0a6a0a34a0a0a0a1a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": [ - 0.0, - 29.0 - ], - "scale": [ - 1.955, - 1.955 - ] - }, - { - "string": "30a-1a2a0.3a13a90a90a29a0a11a0a0a0a0a0a0a0a6a1a0a0a0.254902a0a0.333333a0a0.388235a0a1a0a2a1a0a0a0.254902a0a0.333333a0a0.388235a0a1a0a0a0a0.5a0a6a0a34a0a0a0a1a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": [ - 0.0, - 29.0 - ], - "scale": [ - 1.955, - 1.955 - ] - } + { "string": "48a-1a2a0.3a20a90a2a19a0a10a20a0a0a0a0a0a0a4a1a0a0a0.666667a0a0.360784a0a0.603922a0a1a0a2a1a0a0a0.666667a0a0.360784a0a0.603922a0a1a0a0.5a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a31a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": [-28.0, -65.0], "scale": [2.18, 4.003] }, + { "string": "48a-1a2a0.3a20a90a2a19a0a10a20a0a0a0a0a0a0a4a1a0a0a0.454902a0a0.215686a0a0.403922a0a1a0a2a1a0a0a0.454902a0a0.215686a0a0.403922a0a1a0a0.5a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": [-28.0, -65.0], "scale": [2.18, 4.003] }, + { "string": "48a-1a2a0.3a20a90a2a19a0a10a20a0a0a0a0a0a0a4a1a0a0a0.243137a0a0.329412a0a0.341176a0a1a0a2a1a0a0a0.243137a0a0.329412a0a0.341176a0a1a0a0.5a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a31a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": [25.0, 65.0], "scale": [2.18, 4.003] }, + { "string": "48a-1a2a0.3a20a90a2a19a0a10a20a0a0a0a0a0a0a4a1a0a0a0.145098a0a0.203922a0a0.207843a0a1a0a2a1a0a0a0.145098a0a0.203922a0a0.207843a0a1a0a0.5a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": [25.0, 65.0], "scale": [2.18, 4.003] }, + { "string": "30a-1a2a0.3a13a90a90a29a0a11a0a0a0a0a0a0a0a6a1a0a0a0.705882a0a0.34902a0a0.698039a0a1a0a2a1a0a0a0.584314a0a0.34902a0a0.705882a0a1a0a0a0a0.5a0a6a0a34a0a0a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": [0.0, 29.0], "scale": [1.955, 1.955], "rotation": 90 }, + { "string": "30a-1a2a0.3a13a90a90a29a0a11a0a0a0a0a0a0a0a6a1a0a0a0.254902a0a0.333333a0a0.388235a0a1a0a2a1a0a0a0.254902a0a0.333333a0a0.388235a0a1a0a0a0a0.5a0a6a0a34a0a0a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": [0.0, 29.0], "scale": [1.955, 1.955], "rotation": -90 }, + { "string": "30a-1a2a0.3a13a90a90a29a0a11a0a0a0a0a0a0a0a6a1a0a0a0.705882a0a0.34902a0a0.698039a0a1a0a2a1a0a0a0.584314a0a0.34902a0a0.705882a0a1a0a0a0a0.5a0a6a0a34a0a0a0a1a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": [0.0, 29.0], "scale": [1.955, 1.955], "rotation": 90 }, + { "string": "30a-1a2a0.3a13a90a90a29a0a11a0a0a0a0a0a0a0a6a1a0a0a0.254902a0a0.333333a0a0.388235a0a1a0a2a1a0a0a0.254902a0a0.333333a0a0.388235a0a1a0a0a0a0.5a0a6a0a34a0a0a0a1a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": [0.0, 29.0], "scale": [1.955, 1.955], "rotation": -90 } ] }, "Monster": { - "background": { - "sprite": "monster_gauntlet.png", - "scale": 1.27 - }, - "gauntlet": { - "position": [ - 0.0, - 22.0 - ], - "scale": 1.0 - }, + "background": { "sprite": "monster_gauntlet.png", "scale": 1.27 }, + "gauntlet": { "position": [0.0, 22.0], "scale": 1.075, "zOrder": 2 }, "particles": [ - { - "string": "30a-1a3a0.3a9a90a9a11a0a17a40a0a0a0a0a0a0a4a1a0a0a0.0509804a0a0.929412a0a0a0a1a0a2a1a0a0a0.360784a0a0.486275a0a0.352941a0a1a0a0.1a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a165a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": null, - "scale": [ - 2.51, - 1.985 - ] - }, - { - "string": "66a-1a3a0.3a20a90a27a11a0a37a38a0a0a0a0a0a0a5a1a0a0a0.0156863a0a0.866667a0a0.027451a0a1a0a3a1a0a0a0.164706a0a0.509804a0a0a0a1a0a0.5a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": null, - "scale": [ - 1.072, - 2.475 - ] - }, - { - "string": "18a-1a3a0.3a5a90a9a11a0a32a28a0a0a0a0a0a0a50a1a0a0a0.0352941a0a0.0901961a0a0.0313726a0a1a0a50a1a0a0a0.0352941a0a0.0901961a0a0.0313726a0a1a0a1a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": null, - "scale": [ - 1.47, - 1.957 - ] - } + { "string": "30a-1a3a0.3a9a90a9a11a0a17a40a0a0a0a0a0a0a4a1a0a0a0.0509804a0a0.929412a0a0a0a1a0a2a1a0a0a0.360784a0a0.486275a0a0.352941a0a1a0a0.1a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a165a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": [0.0, -37.0], "scale": [2.51, 1.985] }, + { "string": "66a-1a3a0.3a20a90a27a11a0a37a38a0a0a0a0a0a0a5a1a0a0a0.0156863a0a0.866667a0a0.027451a0a1a0a3a1a0a0a0.164706a0a0.509804a0a0a0a1a0a0.5a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": [0.0, -38.0], "scale": [1.072, 2.475] }, + { "string": "18a-1a3a0.3a5a90a9a11a0a32a28a0a0a0a0a0a0a50a1a0a0a0.0352941a0a0.0901961a0a0.0313726a0a1a0a50a1a0a0a0.0352941a0a0.0901961a0a0.0313726a0a1a0a1a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": [0.0, -48.0], "scale": [1.47, 1.957] } ] }, "Mystery": { - "background": { - "sprite": "mystery_gauntlet.png", - "scale": 1.27 - }, - "gauntlet": { - "position": [ - 3.0, - 13.0 - ], - "scale": 1.0 - }, + "background": { "sprite": "mystery_gauntlet.png", "scale": 1.27 }, + "gauntlet": { "position": [3.0, 13.0], "scale": 0.975, "zOrder": 3 }, "particles": [ - { - "string": "30a-1a3a0.3a9a90a180a29a0a11a0a0a0a0a0a0a0a2a1a0a0a1a0a0.8a0a0.258824a0a1a0a8a1a0a0a1a0a0.8a0a0.258824a0a1a0a0.5a0a0.5a0a6a0a30a0a0a0a1a2a0a0a0a0a11a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": null, - "scale": [ - 2.035, - 2.035 - ] - }, - { - "string": "31a-1a3a0.3a9a90a180a0a0a29a82a0a0a0a0a0a0a4a1a0a0a0.388235a0a0.741176a0a1a0a1a0a15a1a0a0a0.388235a0a0.741176a0a1a0a1a0a1a0a1a0a6a0a30a0a0a0a0a2a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": [ - 0.0, - 0.0 - ], - "scale": [ - 1.435, - 1.435 - ] - } + { "string": "30a-1a3a0.3a9a90a180a29a0a11a0a0a0a0a0a0a0a2a1a0a0a1a0a0.8a0a0.258824a0a1a0a8a1a0a0a1a0a0.8a0a0.258824a0a1a0a0.5a0a0.5a0a6a0a30a0a0a0a1a2a0a0a0a0a11a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": null, "scale": [2.035, 2.035], "zOrder": 2 }, + { "string": "31a-1a3a0.3a9a90a180a0a0a29a82a0a0a0a0a0a0a4a1a0a0a0.388235a0a0.741176a0a1a0a1a0a15a1a0a0a0.388235a0a0.741176a0a1a0a1a0a1a0a1a0a6a0a30a0a0a0a0a2a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": [0.0, 0.0], "scale": [1.435, 1.435] } ] }, "Strange": { - "background": { - "sprite": "strange_gauntlet.png", - "scale": 1.27 - }, - "gauntlet": { - "position": [ - 2.0, - -18.0 - ], - "scale": 1.0 - }, + "background": { "sprite": "strange_gauntlet.png", "scale": 1.27 }, + "reverseGauntlet": true, + "gauntlet": { "position": [2.0, -18.0], "scale": 0.975, "zOrder": 2, "rotation": 180, "flipX": true }, "particles": [ - { - "string": "36a-1a3a0.3a6a-90a24a9a0a11a0a0a-7a0a0a0a0a2a1a76a325a0.592157a0a0.294118a0a0.658824a0a1a0a2a1a201a149a0.592157a0a0.294118a0a0.658824a0a1a0a0.3a0a0.3a0a0a0a0a0a0a0a0a2a0a0a0a0a96a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": [ - 2.0, - -65.0 - ], - "scale": [ - 2.48, - 2.728 - ] - }, - { - "string": "17a-1a4a0.3a3a180a0a8a0a47a34a0a0a0a0a0a0a50a1a0a0a0.243137a0a0.239216a0a0.239216a0a1a0a50a1a0a0a0.454902a0a0.447059a0a0.447059a0a1a0a1a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": [ - 17.0, - 60.0 - ], - "scale": [ - 0.805, - 1.155 - ] - } + { "string": "36a-1a3a0.3a6a-90a24a9a0a11a0a0a-7a0a0a0a0a2a1a76a325a0.592157a0a0.294118a0a0.658824a0a1a0a2a1a201a149a0.592157a0a0.294118a0a0.658824a0a1a0a0.3a0a0.3a0a0a0a0a0a0a0a0a2a0a0a0a0a96a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": [2.0, -65.0], "scale": [2.48, 2.728], "rotation": 180, "zOrder": 2 }, + { "string": "17a-1a4a0.3a3a180a0a8a0a47a34a0a0a0a0a0a0a50a1a0a0a0.243137a0a0.239216a0a0.239216a0a1a0a50a1a0a0a0.454902a0a0.447059a0a0.447059a0a1a0a1a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": [17.0, 60.0], "scale": [0.805, 1.155], "zOrder": 3 } ] }, "Water": { - "background": { - "sprite": "water_gauntlet.png", - "scale": 1.27 - } + "background": { "sprite": "water_gauntlet.png", "scale": 1.27 } }, "Fire": { - "background": { - "sprite": "fire_gauntlet.png", - "scale": 1.27 - }, - "gauntlet": { - "position": [ - 0.0, - 15.0 - ], - "scale": 1.0 - }, + "background": { "sprite": "fire_gauntlet.png", "scale": 1.27 }, + "gauntlet": { "position": [0.0, 15.0], "scale": 1.05, "zOrder": 2 }, "particles": [ - { - "string": "30a-1a2a0.3a13a90a8a21a0a8a0a0a45a0a0a0a0a50a1a89a149a0.396078a0a0.27451a0a0.14902a0a1a0a31a1a145a68a0.141176a0a0.0352941a0a0.0313726a0a1a0a0.5a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": [ - -1.0, - 0.0 - ], - "scale": [ - 1.435, - 0.935 - ] - }, - { - "string": "30a-1a1a0.3a13a90a8a21a0a0a0a0a45a0a0a0a0a21a1a89a149a0.894118a0a0.631373a0a0.345098a0a1a0a13a1a145a68a0.894118a0a0.631373a0a0.345098a0a1a0a0.5a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a183a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": null, - "scale": [ - 2.322, - 1.347 - ] - }, - { - "string": "19a-1a2a0.3a8a90a0a29a0a5a0a0a0a0a0a0a0a50a1a50a33a0.12549a0a0.0862745a0a0a0a1a0a30a1a50a30a0.12549a0a0.0862745a0a0a0a1a0a0.1a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a181a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": null, - "scale": [ - 1.145, - 1.795 - ] - }, - { - "string": "31a-1a3a0.3a9a90a27a11a0a37a38a0a0a0a0a0a0a50a1a0a0a0.133333a0a0.0862745a0a0.0588235a0a1a0a50a1a0a0a0.0666667a0a0.0470588a0a0.0352941a0a1a0a0.5a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": [ - 1.0, - -23.0 - ], - "scale": [ - 1.27, - 1.832 - ] - }, - { - "string": "40a-1a3a0.3a12a90a13a11a0a48a78a0a0a0a0a0a0a5a3a0a0a0.439216a0a0.309804a0a0.129412a0a1a0a3a3a0a0a0.439216a0a0.309804a0a0.129412a0a1a0a0.1a0a0.3a0a0a0a0a0a0a0a0a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": [ - 2.0, - -20.0 - ], - "scale": [ - 1.205, - 1.28 - ] - } + { "string": "30a-1a2a0.3a13a90a8a21a0a8a0a0a45a0a0a0a0a50a1a89a149a0.396078a0a0.27451a0a0.14902a0a1a0a31a1a145a68a0.141176a0a0.0352941a0a0.0313726a0a1a0a0.5a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": [-1.0, 0.0], "scale": [1.435, 0.935] }, + { "string": "30a-1a1a0.3a13a90a8a21a0a0a0a0a45a0a0a0a0a21a1a89a149a0.894118a0a0.631373a0a0.345098a0a1a0a13a1a145a68a0.894118a0a0.631373a0a0.345098a0a1a0a0.5a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a183a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": null, "scale": [2.322, 1.347] }, + { "string": "19a-1a2a0.3a8a90a0a29a0a5a0a0a0a0a0a0a0a50a1a50a33a0.12549a0a0.0862745a0a0a0a1a0a30a1a50a30a0.12549a0a0.0862745a0a0a0a1a0a0.1a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a181a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": null, "scale": [1.145, 1.795] }, + { "string": "31a-1a3a0.3a9a90a27a11a0a37a38a0a0a0a0a0a0a50a1a0a0a0.133333a0a0.0862745a0a0.0588235a0a1a0a50a1a0a0a0.0666667a0a0.0470588a0a0.0352941a0a1a0a0.5a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": [1.0, -23.0], "scale": [1.27, 1.832] }, + { "string": "40a-1a3a0.3a12a90a13a11a0a48a78a0a0a0a0a0a0a5a3a0a0a0.439216a0a0.309804a0a0.129412a0a1a0a3a3a0a0a0.439216a0a0.309804a0a0.129412a0a1a0a0.1a0a0.3a0a0a0a0a0a0a0a0a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": [2.0, -20.0], "scale": [1.205, 1.28] } ] }, "Ice": { - "background": { - "sprite": "ice_gauntlet.png", - "scale": 1.27 - }, - "gauntlet": { - "position": [ - 0.0, - 15.0 - ], - "scale": 1.0 - }, + "background": { "sprite": "ice_gauntlet.png", "scale": 1.27 }, + "gauntlet": { "position": [0.0, 15.0], "scale": 1.05, "zOrder": 2 }, "particles": [ - { - "string": "25a-1a1a0.3a19a90a90a0a0a0a0a0a0a0a0a0a0a12a1a0a98a0.0980392a0a0.176471a0a0.219608a0a1a0a36a1a0a0a0.0980392a0a0.176471a0a0.219608a0a1a0a0a0a0a0a0a0a0a0a0a0a0a2a1a0a0a0a191a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": [ - 0.0, - 17.0 - ], - "scale": [ - 3.76, - 3.76 - ] - }, - { - "string": "200a-1a4a0.3a46a90a0a18a0a24a0a0a0a0a0a0a0a1a1a0a39a0.980392a0a1a0a0.980392a0a1a0a1a1a98a41a0.980392a0a1a0a0.980392a0a1a0a0.3a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a14a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": [ - 11.0, - 112.0 - ], - "scale": [ - 2.322, - 3.097 - ] - }, - { - "string": "44a-1a3a0.3a13a90a27a11a0a40a62a0a0a0a0a0a0a50a1a0a0a0.0431373a0a0.0627451a0a0.0666667a0a1a0a50a1a0a0a0.0431373a0a0.0627451a0a0.0666667a0a1a0a0.5a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": [ - 0.0, - -11.0 - ], - "scale": [ - 1.145, - 1.795 - ] - }, - { - "string": "38a-1a1.5a0.3a21a90a90a0a0a31a69a0a0a0a0a0a0a2a1a65a57a1a0a1a0a1a0a1a0a5a1a0a0a1a0a1a0a1a0a1a0a0.2a0a0.4a0a0a0a0a0a0a0a0a2a1a0a0a0a169a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": null, - "scale": [ - 1.47, - 1.957 - ] - } + { "string": "25a-1a1a0.3a19a90a90a0a0a0a0a0a0a0a0a0a0a12a1a0a98a0.0980392a0a0.176471a0a0.219608a0a1a0a36a1a0a0a0.0980392a0a0.176471a0a0.219608a0a1a0a0a0a0a0a0a0a0a0a0a0a0a2a1a0a0a0a191a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": [0.0, 17.0], "scale": [3.76, 3.76] }, + { "string": "200a-1a4a0.3a46a90a0a18a0a24a0a0a0a0a0a0a0a1a1a0a39a0.980392a0a1a0a0.980392a0a1a0a1a1a98a41a0.980392a0a1a0a0.980392a0a1a0a0.3a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a14a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": [11.0, 112.0], "scale": [2.322, 3.097], "rotation": -174 }, + { "string": "44a-1a3a0.3a13a90a27a11a0a40a62a0a0a0a0a0a0a50a1a0a0a0.0431373a0a0.0627451a0a0.0666667a0a1a0a50a1a0a0a0.0431373a0a0.0627451a0a0.0666667a0a1a0a0.5a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": [0.0, -11.0], "scale": [1.145, 1.795] }, + { "string": "38a-1a1.5a0.3a21a90a90a0a0a31a69a0a0a0a0a0a0a2a1a65a57a1a0a1a0a1a0a1a0a5a1a0a0a1a0a1a0a1a0a1a0a0.2a0a0.4a0a0a0a0a0a0a0a0a2a1a0a0a0a169a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": null, "scale": [1.47, 1.957] } ] }, "Poison": { - "background": { - "sprite": "poison_gauntlet.png", - "scale": 1.27 - }, + "background": { "sprite": "poison_gauntlet.png", "scale": 1.27 }, "particles": [ - { - "string": "31a-1a3a0.3a9a90a27a11a0a37a64a0a0a0a0a0a0a50a1a0a0a0.054902a0a0.129412a0a0.0627451a0a1a0a50a1a0a0a0.054902a0a0.129412a0a0.0627451a0a1a0a0.5a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": [ - 0.0, - 11.0 - ], - "scale": [ - 1.26, - 1.485 - ] - }, - { - "string": "20a-1a3a0.3a6a90a22a11a0a33a55a0a0a0a0a0a0a1a1a0a0a0.137255a0a0.301961a0a0.133333a0a1a0a10a1a0a0a0.137255a0a0.301961a0a0.133333a0a1a0a0.1a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a40a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": [ - -3.0, - -13.0 - ], - "scale": [ - 1.722, - 1.397 - ] - }, - { - "string": "4a-1a2.5a0.3a1a-90a0a0a10a35a0a0a-70a0a0a0a0a6a2a140a0a0.345098a0a0.690196a0a0.301961a0a1a0a4a2a140a0a0.345098a0a0.690196a0a0.301961a0a1a0a0a0a0a0a0a0a0a0a0a0a0a2a1a0a0a0a77a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": [ - 0.0, - 109.0 - ], - "scale": [ - 1.145, - 1.795 - ] - }, - { - "string": "40a-1a3a0.3a12a90a13a11a0a48a78a0a0a0a0a0a0a3a1a0a0a0.129412a0a0.439216a0a0.168627a0a1a0a1a1a0a0a0.129412a0a0.439216a0a0.168627a0a1a0a0.1a0a0.3a0a0a0a0a0a0a0a0a2a1a0a0a0a192a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": null, - "scale": [ - 1.27, - 1.632 - ] - } + { "string": "31a-1a3a0.3a9a90a27a11a0a37a64a0a0a0a0a0a0a50a1a0a0a0.054902a0a0.129412a0a0.0627451a0a1a0a50a1a0a0a0.054902a0a0.129412a0a0.0627451a0a1a0a0.5a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": [0.0, 11.0], "scale": [1.26, 1.485] }, + { "string": "20a-1a3a0.3a6a90a22a11a0a33a55a0a0a0a0a0a0a1a1a0a0a0.137255a0a0.301961a0a0.133333a0a1a0a10a1a0a0a0.137255a0a0.301961a0a0.133333a0a1a0a0.1a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a40a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": [-3.0, -13.0], "scale": [1.722, 1.397] }, + { "string": "4a-1a2.5a0.3a1a-90a0a0a10a35a0a0a-70a0a0a0a0a6a2a140a0a0.345098a0a0.690196a0a0.301961a0a1a0a4a2a140a0a0.345098a0a0.690196a0a0.301961a0a1a0a0a0a0a0a0a0a0a0a0a0a0a2a1a0a0a0a77a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": [0.0, 109.0], "scale": [1.145, 1.795] }, + { "string": "40a-1a3a0.3a12a90a13a11a0a48a78a0a0a0a0a0a0a3a1a0a0a0.129412a0a0.439216a0a0.168627a0a1a0a1a1a0a0a0.129412a0a0.439216a0a0.168627a0a1a0a0.1a0a0.3a0a0a0a0a0a0a0a0a2a1a0a0a0a192a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": null, "scale": [1.27, 1.632] } ] }, "Shadow": { - "background": { - "sprite": "shadow_gauntlet.png", - "scale": 1.745 - }, - "gauntlet": { - "position": [ - 2.0, - 26.0 - ], - "scale": 1.0 - }, + "background": { "sprite": "shadow_gauntlet.png", "scale": 1.745 }, + "gauntlet": { "position": [2.0, 26.0], "scale": 0.975, "zOrder": 2 }, "particles": [ - { - "string": "48a-1a3a0.3a14a90a27a6a0a40a57a0a0a0a0a0a0a50a1a0a0a0.0509804a0a0.0235294a0a0.0666667a0a1a0a50a1a0a0a0.0509804a0a0.0235294a0a0.0666667a0a1a0a0.5a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": [ - 0.0, - -15.0 - ], - "scale": [ - 1.31, - 1.735 - ] - }, - { - "string": "62a-1a1a0.3a47a90a90a29a0a11a0a0a0a0a0a0a0a50a1a0a59a0.0784314a0a0.0235294a0a0.0941176a0a1a0a38a1a103a65a0.0784314a0a0.0235294a0a0.0941176a0a1a0a0.5a0a0.5a0a0a0a0a0a0a0a1a2a1a0a0a0a190a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": [ - 0.0, - 25.0 - ], - "scale": [ - 2.472, - 2.472 - ] - }, - { - "string": "62a-1a1a0.3a47a90a180a29a0a11a0a0a0a0a0a0a0a50a1a0a0a0.0313726a0a0.00392157a0a0.0470588a0a1a0a38a1a0a0a0.0313726a0a0.00392157a0a0.0470588a0a1a0a0.5a0a0.5a0a38a0a9a0a-85a26a1a2a1a0a0a0a183a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": [ - 0.0, - 25.0 - ], - "scale": [ - 1.47, - 1.47 - ] - }, - { - "string": "62a-1a1a0.3a47a90a180a29a0a11a0a0a0a0a0a0a0a50a1a0a0a0.0313726a0a0.00392157a0a0.0431373a0a1a0a38a1a0a0a0.0313726a0a0.00392157a0a0.0431373a0a1a0a0.5a0a0.5a0a38a0a9a0a-85a26a1a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": [ - 0.0, - 25.0 - ], - "scale": [ - 1.195, - 1.195 - ] - }, - { - "string": "40a-1a3a0.3a12a90a13a11a0a48a78a0a0a0a0a0a0a5a3a0a0a0.129412a0a0.439216a0a0.133333a0a1a0a3a3a0a0a0.129412a0a0.439216a0a0.133333a0a1a0a0.1a0a0.3a0a0a0a0a0a0a0a0a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": [ - 0.0, - -16.0 - ], - "scale": [ - 1.255, - 1.43 - ] - } + { "string": "48a-1a3a0.3a14a90a27a6a0a40a57a0a0a0a0a0a0a50a1a0a0a0.0509804a0a0.0235294a0a0.0666667a0a1a0a50a1a0a0a0.0509804a0a0.0235294a0a0.0666667a0a1a0a0.5a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": [0.0, -15.0], "scale": [1.31, 1.735] }, + { "string": "62a-1a1a0.3a47a90a90a29a0a11a0a0a0a0a0a0a0a50a1a0a59a0.0784314a0a0.0235294a0a0.0941176a0a1a0a38a1a103a65a0.0784314a0a0.0235294a0a0.0941176a0a1a0a0.5a0a0.5a0a0a0a0a0a0a0a1a2a1a0a0a0a190a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": [0.0, 25.0], "scale": [2.472, 2.472] }, + { "string": "62a-1a1a0.3a47a90a180a29a0a11a0a0a0a0a0a0a0a50a1a0a0a0.0313726a0a0.00392157a0a0.0470588a0a1a0a38a1a0a0a0.0313726a0a0.00392157a0a0.0470588a0a1a0a0.5a0a0.5a0a38a0a9a0a-85a26a1a2a1a0a0a0a183a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": [0.0, 25.0], "scale": [1.47, 1.47] }, + { "string": "62a-1a1a0.3a47a90a180a29a0a11a0a0a0a0a0a0a0a50a1a0a0a0.0313726a0a0.00392157a0a0.0431373a0a1a0a38a1a0a0a0.0313726a0a0.00392157a0a0.0431373a0a1a0a0.5a0a0.5a0a38a0a9a0a-85a26a1a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": [0.0, 25.0], "scale": [1.195, 1.195] }, + { "string": "40a-1a3a0.3a12a90a13a11a0a48a78a0a0a0a0a0a0a5a3a0a0a0.129412a0a0.439216a0a0.133333a0a1a0a3a3a0a0a0.129412a0a0.439216a0a0.133333a0a1a0a0.1a0a0.3a0a0a0a0a0a0a0a0a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": [0.0, -16.0], "scale": [1.255, 1.43] } ] }, "Lava": { - "background": { - "sprite": "lava_gauntlet.png", - "scale": 1.27 - }, - "gauntlet": { - "position": [ - 0.0, - 16.0 - ], - "scale": 1.0 - }, + "background": { "sprite": "lava_gauntlet.png", "scale": 1.27 }, + "gauntlet": { "position": [0.0, 16.0], "scale": 0.95, "zOrder": 2 }, + "particles": [ + { "string": "40a-1a3a0.3a12a90a13a11a0a48a78a0a0a0a0a0a0a5a3a0a0a0.439216a0a0.309804a0a0.129412a0a1a0a3a3a0a0a0.439216a0a0.309804a0a0.129412a0a1a0a0.1a0a0.3a0a0a0a0a0a0a0a0a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": [0.0, -16.0], "scale": [1.36, 1.36] }, + { "string": "59a-1a1a0.3a45a90a14a100a54a2a6a0a-135a0a46a0a0a20a8a0a0a1a0a0.756863a0a0.258824a0a1a0a1a1a0a0a1a0a0.694118a0a0.321569a0a1a0a0a0a0.1a0a0a0a0a0a0a0a0a2a1a0a0a1a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": null, "scale": [1.522, 1.547] }, + { "string": "56a-1a1a0.3a43a90a16a103a81a2a6a0a-180a0a46a0a0a13a8a0a0a1a0a0.756863a0a0.258824a0a1a0a1a1a0a0a1a0a0.694118a0a0.321569a0a1a0a0a0a0.1a0a0a0a0a0a0a0a0a2a1a0a0a1a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": null, "scale": [1.47, 1.37] }, + { "string": "56a-1a1a0.5a19a90a28a49a43a2a6a0a23a0a43a0a0a50a33a0a0a0.172549a0a0.160784a0a0.14902a0a0.47a0a50a38a0a0a0.247059a0a0.243137a0a0.239216a0a0.47a0a0a0a0.13a0a0a0a0a0a0a0a0a2a0a0a0a1a29a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": null, "scale": [1.095, 0.845] }, + { "string": "59a-1a1a0.3a45a90a18a113a30a2a6a0a-192a0a46a0a0a20a8a0a0a1a0a0.505882a0a0.258824a0a1a0a1a1a0a0a1a0a0.505882a0a0.258824a0a1a0a0a0a0.1a0a0a0a0a0a0a0a0a2a1a0a0a1a158a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": null, "scale": [1.255, 1.43] }, + { "string": "48a-1a3a0.3a14a90a27a6a0a40a57a0a0a0a0a0a0a50a1a0a0a0.0666667a0a0.0352941a0a0.0196078a0a1a0a50a1a0a0a0.0666667a0a0.0352941a0a0.0196078a0a1a0a0.5a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": [0.0, -18.0], "scale": [1.235, 1.835] } + ] + }, + "Time": { "background": { "sprite": "time_gauntlet.png", "scale": 1.22 } }, + "Spike": { "background": { "sprite": "spike_gauntlet.png", "scale": 1.92 } }, + "Cyborg": { "background": { "sprite": "cyborg_gauntlet.png", "scale": 1.27 } }, + "Inferno": { "background": { "sprite": "inferno_gauntlet.png", "scale": 1.27 } }, + "Death": { "background": { "sprite": "death_gauntlet.png", "scale": 1.27 } }, + "Magic": { "background": { "sprite": "magic_gauntlet.png", "scale": 1.27 } }, + "Chaos": { "background": { "sprite": "chaos_gauntlet.png", "scale": 1.27 } }, + "Force": { "background": { "sprite": "force_gauntlet.png", "scale": 1.27 } }, + "NCS I": { + "background": { "sprite": "ncs_gauntlet_1.png", "scale": 1.27 }, + "gauntlet": { "position": [0.0, 16.0], "scale": 0.9, "zOrder": 3 }, "particles": [ - { - "string": "40a-1a3a0.3a12a90a13a11a0a48a78a0a0a0a0a0a0a5a3a0a0a0.439216a0a0.309804a0a0.129412a0a1a0a3a3a0a0a0.439216a0a0.309804a0a0.129412a0a1a0a0.1a0a0.3a0a0a0a0a0a0a0a0a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": [ - 0.0, - -16.0 - ], - "scale": [ - 1.36, - 1.36 - ] - }, - { - "string": "59a-1a1a0.3a45a90a14a100a54a2a6a0a-135a0a46a0a0a20a8a0a0a1a0a0.756863a0a0.258824a0a1a0a1a1a0a0a1a0a0.694118a0a0.321569a0a1a0a0a0a0.1a0a0a0a0a0a0a0a0a2a1a0a0a1a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": null, - "scale": [ - 1.522, - 1.547 - ] - }, - { - "string": "56a-1a1a0.3a43a90a16a103a81a2a6a0a-180a0a46a0a0a13a8a0a0a1a0a0.756863a0a0.258824a0a1a0a1a1a0a0a1a0a0.694118a0a0.321569a0a1a0a0a0a0.1a0a0a0a0a0a0a0a0a2a1a0a0a1a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": null, - "scale": [ - 1.47, - 1.37 - ] - }, - { - "string": "56a-1a1a0.5a19a90a28a49a43a2a6a0a23a0a43a0a0a50a33a0a0a0.172549a0a0.160784a0a0.14902a0a0.47a0a50a38a0a0a0.247059a0a0.243137a0a0.239216a0a0.47a0a0a0a0.13a0a0a0a0a0a0a0a0a2a0a0a0a1a29a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": null, - "scale": [ - 1.095, - 0.845 - ] - }, - { - "string": "59a-1a1a0.3a45a90a18a113a30a2a6a0a-192a0a46a0a0a20a8a0a0a1a0a0.505882a0a0.258824a0a1a0a1a1a0a0a1a0a0.505882a0a0.258824a0a1a0a0a0a0.1a0a0a0a0a0a0a0a0a2a1a0a0a1a158a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": null, - "scale": [ - 1.255, - 1.43 - ] - }, - { - "string": "48a-1a3a0.3a14a90a27a6a0a40a57a0a0a0a0a0a0a50a1a0a0a0.0666667a0a0.0352941a0a0.0196078a0a1a0a50a1a0a0a0.0666667a0a0.0352941a0a0.0196078a0a1a0a0.5a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": [ - 0.0, - -18.0 - ], - "scale": [ - 1.235, - 1.835 - ] - } + { "string": "25a-1a1a0.3a19a90a90a0a0a0a0a0a0a0a0a0a0a23a1a0a98a0.0980392a0a0.176471a0a0.219608a0a1a0a50a1a0a0a0.0980392a0a0.176471a0a0.219608a0a1a0a0a0a0a0a0a0a0a0a0a0a0a2a1a0a0a0a185a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": [0.0, 20.0], "scale": [2.06, 2.06], "zOrder": 2 }, + { "string": "25a-1a1a0.3a19a90a90a0a0a0a0a0a0a0a0a0a0a12a1a0a98a0.0980392a0a0.176471a0a0.219608a0a1a0a36a1a0a0a0.0980392a0a0.176471a0a0.219608a0a1a0a0a0a0a0a0a0a0a0a0a0a0a2a1a0a0a0a167a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": null, "scale": [4.21, 4.21], "zOrder": 2 }, + { "string": "30a-1a2.5a0.3a10a90a12a19a0a23a0a0a11a0a0a0a0a4a1a0a0a0.313726a0a0.458824a0a0.529412a0a1a0a2a1a0a0a0.313726a0a0.458824a0a0.529412a0a1a0a0a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a19a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": [0.0, -81.0], "scale": [2.035, 2.035], "zOrder": 0 } ] }, - "Time": { - "background": { - "sprite": "time_gauntlet.png", - "scale": 1.22 - } - }, - "Spike": { - "background": { - "sprite": "spike_gauntlet.png", - "scale": 1.92 - } - }, - "Cyborg": { - "background": { - "sprite": "cyborg_gauntlet.png", - "scale": 1.27 - } - }, - "Inferno": { - "background": { - "sprite": "inferno_gauntlet.png", - "scale": 1.27 - } - }, - "Death": { - "background": { - "sprite": "death_gauntlet.png", - "scale": 1.27 - } - }, - "Magic": { - "background": { - "sprite": "magic_gauntlet.png", - "scale": 1.27 - } + "NCS II": { + "background": { "sprite": "ncs_gauntlet_2.png", "scale": 1.27 }, + "gauntlet": { "position": [0.0, 16.0], "scale": 0.9, "zOrder": 3 }, + "particles": [ + { "string": "25a-1a1a0.3a19a90a90a0a0a0a0a0a0a0a0a0a0a23a1a0a98a0.301961a0a0.156863a0a0.341176a0a1a0a50a1a0a0a0.301961a0a0.156863a0a0.341176a0a1a0a0a0a0a0a0a0a0a0a0a0a0a2a1a0a0a0a185a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": [0.0, 20.0], "scale": [2.06, 2.06], "zOrder": 2 }, + { "string": "25a-1a1a0.3a19a90a90a0a0a0a0a0a0a0a0a0a0a12a1a0a98a0.301961a0a0.156863a0a0.341176a0a1a0a36a1a0a0a0.301961a0a0.156863a0a0.341176a0a1a0a0a0a0a0a0a0a0a0a0a0a0a2a1a0a0a0a167a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": null, "scale": [4.21, 4.21], "zOrder": 2 }, + { "string": "30a-1a2.5a0.3a10a90a12a19a0a23a0a0a11a0a0a0a0a4a1a0a0a0.482353a0a0.317647a0a0.533333a0a1a0a2a1a0a0a0.482353a0a0.317647a0a0.533333a0a1a0a0a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a19a0a0a0a0a0a0a0a0a0a0a0a0a0a0", "position": [0.0, -81.0], "scale": [2.035, 2.035], "zOrder": 0 } + ] }, - "Chaos": { - "background": { - "sprite": "chaos_gauntlet.png", - "scale": 1.27 - } + "Discord": { + "background": { "sprite": "discord_gauntlet.png", "scale": 1.245, "offsetY": 3 }, + "itemZOrder": 1 }, - "Force": { - "background": { - "sprite": "force_gauntlet.png", - "scale": 1.27 - } + "Space": { + "background": { "sprite": "space_gauntlet.png", "scale": 1.245, "offsetY": 3 }, + "itemZOrder": 1 }, - "NCS I": { - "background": { - "sprite": "ncs_gauntlet_1.png", - "scale": 1.27 - }, - "gauntlet": { - "position": [ - 0.0, - 16.0 - ], - "scale": 1.0 - }, - "particles": [ - { - "string": "25a-1a1a0.3a19a90a90a0a0a0a0a0a0a0a0a0a0a23a1a0a98a0.0980392a0a0.176471a0a0.219608a0a1a0a50a1a0a0a0.0980392a0a0.176471a0a0.219608a0a1a0a0a0a0a0a0a0a0a0a0a0a0a2a1a0a0a0a185a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": [ - 0.0, - 20.0 - ], - "scale": [ - 1.0, - 1.0 - ] - }, - { - "string": "25a-1a1a0.3a19a90a90a0a0a0a0a0a0a0a0a0a0a12a1a0a98a0.0980392a0a0.176471a0a0.219608a0a1a0a36a1a0a0a0.0980392a0a0.176471a0a0.219608a0a1a0a0a0a0a0a0a0a0a0a0a0a0a2a1a0a0a0a167a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": null, - "scale": [ - 1.0, - 1.0 - ] - }, - { - "string": "30a-1a2.5a0.3a10a90a12a19a0a23a0a0a11a0a0a0a0a4a1a0a0a0.313726a0a0.458824a0a0.529412a0a1a0a2a1a0a0a0.313726a0a0.458824a0a0.529412a0a1a0a0a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a19a0a0a0a0a0a0a0a0a0a0a0a0a0a0", - "position": [ - 0.0, - -81.0 - ], - "scale": [ - 1.0, - 1.0 - ] - } - ] + "Cosmos": { + "background": { "sprite": "cosmos_gauntlet.png", "scale": 1.245, "offsetY": 3 }, + "itemZOrder": 1 } } diff --git a/src/hooks/GauntletSelectLayer.cpp b/src/hooks/GauntletSelectLayer.cpp index 365e6d3..1e4f94a 100644 --- a/src/hooks/GauntletSelectLayer.cpp +++ b/src/hooks/GauntletSelectLayer.cpp @@ -1,19 +1,27 @@ #include #include - struct GauntletParticles { std::string particle; CCPoint pos; - CCPoint scale = CCPoint(1.310f, 2.610f); + CCPoint scale = CCPoint(1.0f, 1.0f); + float rotation = 0.0f; + int zOrder = 1; + bool useGauntletPos = true; }; struct NewGauntlet { std::vector particles; - std::string_view background; - CCPoint bgPos; - float scaleBg; - float scaleGauntlet; + std::string background; + float bgOffsetY = 0.0f; + float scaleBg = 1.0f; + float scaleGauntlet = 0.0f; + CCPoint gauntletPos; + float gauntletRotation = 0.0f; + bool gauntletFlipX = false; + int gauntletZOrder = 2; + bool reverseGauntlet = false; + int itemZOrder = 0; }; template<> @@ -22,24 +30,65 @@ struct matjson::Serialize { NewGauntlet data; GEODE_UNWRAP_INTO(data.background, value["background"]["sprite"].asString()); GEODE_UNWRAP_INTO(data.scaleBg, value["background"]["scale"].asDouble()); + + if (value["background"].contains("offsetY")) { + data.bgOffsetY = static_cast(value["background"]["offsetY"].asDouble().unwrapOrDefault()); + } + if (value.contains("reverseGauntlet")) { + data.reverseGauntlet = value["reverseGauntlet"].asBool().unwrapOrDefault(); + } + if (value.contains("itemZOrder")) { + data.itemZOrder = value["itemZOrder"].asInt().unwrapOrDefault(); + } if (value.contains("gauntlet")) { - GEODE_UNWRAP_INTO(auto bgPos, value["gauntlet"]["position"].asArray()); - data.bgPos = CCPoint(bgPos.at(0).asDouble().unwrapOrDefault(), bgPos.at(1).asDouble().unwrapOrDefault()); - GEODE_UNWRAP_INTO(data.scaleGauntlet, value["gauntlet"]["scale"].asDouble()); + auto const& g = value["gauntlet"]; + if (g.contains("position")) { + GEODE_UNWRAP_INTO(auto gPos, g["position"].asArray()); + data.gauntletPos = CCPoint( + static_cast(gPos.at(0).asDouble().unwrapOrDefault()), + static_cast(gPos.at(1).asDouble().unwrapOrDefault()) + ); + } + GEODE_UNWRAP_INTO(data.scaleGauntlet, g["scale"].asDouble()); + if (g.contains("zOrder")) { + data.gauntletZOrder = g["zOrder"].asInt().unwrapOrDefault(); + } + if (g.contains("rotation")) { + data.gauntletRotation = static_cast(g["rotation"].asDouble().unwrapOrDefault()); + } + if (g.contains("flipX")) { + data.gauntletFlipX = g["flipX"].asBool().unwrapOrDefault(); + } } if (value.contains("particles")) { GEODE_UNWRAP_INTO(auto particles, value["particles"].asArray()); - for (auto& particle : particles) { - GauntletParticles particleData; - GEODE_UNWRAP_INTO(particleData.particle, particle["string"].asString()); - auto posOpt = particle["position"].asArray(); + for (auto const& p : particles) { + GauntletParticles pd; + GEODE_UNWRAP_INTO(pd.particle, p["string"].asString()); + + auto posOpt = p["position"].asArray(); if (posOpt.isOk()) { - auto pos = posOpt.unwrap(); - particleData.pos = CCPoint(pos.at(0).asDouble().unwrapOrDefault(), pos.at(1).asDouble().unwrapOrDefault()); + auto const& pos = posOpt.unwrap(); + pd.pos = CCPoint( + static_cast(pos.at(0).asDouble().unwrapOrDefault()), + static_cast(pos.at(1).asDouble().unwrapOrDefault()) + ); + pd.useGauntletPos = false; + } + if (p.contains("scale")) { + GEODE_UNWRAP_INTO(auto sc, p["scale"].asArray()); + pd.scale = CCPoint( + static_cast(sc.at(0).asDouble().unwrapOrDefault()), + static_cast(sc.at(1).asDouble().unwrapOrDefault()) + ); + } + if (p.contains("zOrder")) { + pd.zOrder = p["zOrder"].asInt().unwrapOrDefault(); } - GEODE_UNWRAP_INTO(auto scale, particle["scale"].asArray()); - particleData.scale = CCPoint(scale.at(0).asDouble().unwrapOrDefault(), scale.at(1).asDouble().unwrapOrDefault()); - data.particles.push_back(particleData); + if (p.contains("rotation")) { + pd.rotation = static_cast(p["rotation"].asDouble().unwrapOrDefault()); + } + data.particles.push_back(std::move(pd)); } } return geode::Ok(data); @@ -51,39 +100,28 @@ class $modify(CustomGauntletSelectLayer, GauntletSelectLayer) { if (!node->m_gauntletInfoNode) return; if (auto checkmark = node->getChildByID("checkmark-sprite")) { checkmark->setPositionY(78); - CCNode* checkmark_shadow = node->getChildByID("checkmark-shadow-sprite"); - checkmark_shadow->setPositionY(78); + if (auto shadow = node->getChildByID("checkmark-shadow-sprite")) shadow->setPositionY(78); } if (auto label_reward = node->m_gauntletInfoNode->getChildByID("reward-label")) { label_reward->setPositionY(50); - CCNode* label_reward_shadow = node->m_gauntletInfoNode->getChildByID("reward-shadow-label"); - label_reward_shadow->setPositionY(50); - - if (CCSprite* chest = static_cast(node->m_gauntletInfoNode->getChildByID("chest-sprite"))) { + if (auto s = node->m_gauntletInfoNode->getChildByID("reward-shadow-label")) s->setPositionY(50); + if (auto chest = static_cast(node->m_gauntletInfoNode->getChildByID("chest-sprite"))) { chest->setPositionY(80); chest->setFlipY(true); } - if (CCSprite* chest_shadow = static_cast(node->m_gauntletInfoNode->getChildByID("chest-shadow-sprite"))) { - chest_shadow->setPositionY(80); - chest_shadow->setFlipY(true); + if (auto chest_s = static_cast(node->m_gauntletInfoNode->getChildByID("chest-shadow-sprite"))) { + chest_s->setPositionY(80); + chest_s->setFlipY(true); } } - if (CCNode* label_progress = node->m_gauntletInfoNode->getChildByID("gauntlet-progress-label")) { - label_progress->setPositionY(29); - } - if (CCNode* label_progress_shadow = node->m_gauntletInfoNode->getChildByID("gauntlet-progress-shadow-label")) { - label_progress_shadow->setPositionY(29); - } + if (auto lp = node->m_gauntletInfoNode->getChildByID("gauntlet-progress-label")) lp->setPositionY(29); + if (auto lps = node->m_gauntletInfoNode->getChildByID("gauntlet-progress-shadow-label")) lps->setPositionY(29); } bool init(int p0) { if (!GauntletSelectLayer::init(p0)) return false; - CCPoint bcrPos; - if (auto node = this->getChildByIDRecursive("bottom-right-corner")) { - bcrPos = node->getPosition(); - } if (auto menu = this->getChildByIDRecursive("bottom-right-menu")) { - Build::createSpriteName("GJ_infoIcon_001.png").color(236,149,19).intoMenuItem([]() { + Build::createSpriteName("GJ_infoIcon_001.png").color(236, 149, 19).intoMenuItem([]() { FLAlertLayer::create( nullptr, "Gauntlets redesign", @@ -98,9 +136,12 @@ Thanks for their contributions! 300.0f )->show(); }).id("particle-info-button"_spr).zOrder(10).with([](CCNode* node) { - CCParticleSystemQuad* particle_info = GameToolbox::particleFromString("26a-1a1.66a0.3a30a90a180a14a0a1a1a0a7a0a0a0a0a2a1a0a0a1a0a0.568627a0a0.12549a0a1a0a1a1a0a0a1a0a0.501961a0a0a0a1a0a0a0a0.46a0a0a0a0a0a0a0a0a2a1a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChildAtPosition(particle_info, Anchor::Center); - particle_info->setZOrder(9); + auto* p = GameToolbox::particleFromString( + "26a-1a1.66a0.3a30a90a180a14a0a1a1a0a7a0a0a0a0a2a1a0a0a1a0a0.568627a0a0.12549a0a1a0a1a1a0a0a1a0a0.501961a0a0a0a1a0a0a0a0.46a0a0a0a0a0a0a0a0a2a1a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + nullptr, false + ); + node->addChildAtPosition(p, Anchor::Center); + p->setZOrder(9); }).parent(menu); menu->updateLayout(); } @@ -110,16 +151,24 @@ Thanks for their contributions! void setupGauntlets() { GauntletSelectLayer::setupGauntlets(); if (!m_scrollLayer) return; + auto res = file::readJson(Mod::get()->getResourcesDir() / "particles.json"); - if (res.isErr()) return log::error("Couldn't read particles.json: {}", res.unwrapErr()); + if (res.isErr()) { + log::error("Couldn't read gauntlets/particles.json: {}", res.unwrapErr()); + return; + } + auto const& json = res.unwrap(); + auto const scaleFactor = 4.f / CCDirector::get()->getContentScaleFactor(); for (int i = 0; i < m_scrollLayer->m_pages->count(); i++) { - auto page = static_cast(static_cast(m_scrollLayer->m_pages->objectAtIndex(i))->getChildren()->objectAtIndex(0))->getChildren(); + auto* page = static_cast( + static_cast(m_scrollLayer->m_pages->objectAtIndex(i)) + ->getChildren()->objectAtIndex(0) + )->getChildren(); for (int j = 0; j < page->count(); j++) { - CCNode* item = static_cast(page->objectAtIndex(j)); + auto* item = static_cast(page->objectAtIndex(j)); - // Handle spaces if (j == 0 && page->count() > 1) { item->setPositionX(item->getPositionX() - 25.0f); } else if (j == 1 && page->count() == 2) { @@ -128,17 +177,15 @@ Thanks for their contributions! item->setPositionX(item->getPositionX() + 25.0f); } - GauntletNode* node = static_cast(item->getChildByIDRecursive("gauntlet-node")); - - CCLabelBMFont* label = static_cast(node->getChildByID("gauntlet-name-label")); - CCNode* label_shadow = node->getChildByID("gauntlet-name-shadow-label"); - CCNode* label_gauntlet = node->getChildByID("gauntlet-label"); - CCNode* label_shadow_gauntlet = node->getChildByID("gauntlet-shadow-label"); - CCNode* bg = node->getChildByID("background"); - - CCNode* gauntlet_node = node->m_gauntletInfoNode; - CCSprite* gauntlet = static_cast(gauntlet_node->getChildByID("gauntlet-sprite")); - CCNode* gauntlet_shadow = gauntlet_node->getChildByID("gauntlet-shadow-sprite"); + auto* node = static_cast(item->getChildByIDRecursive("gauntlet-node")); + auto* label = static_cast(node->getChildByID("gauntlet-name-label")); + auto* label_shadow = node->getChildByID("gauntlet-name-shadow-label"); + auto* label_gauntlet = node->getChildByID("gauntlet-label"); + auto* label_shadow_gauntlet = node->getChildByID("gauntlet-shadow-label"); + auto* bg = node->getChildByID("background"); + auto* gauntlet_node = node->m_gauntletInfoNode; + auto* gauntlet = static_cast(gauntlet_node->getChildByID("gauntlet-sprite")); + auto* gauntlet_shadow = gauntlet_node->getChildByID("gauntlet-shadow-sprite"); bg->setVisible(false); label->setVisible(false); @@ -150,60 +197,7 @@ Thanks for their contributions! std::string gauntletName = label->getString(); - auto scaleFactor = 4/CCDirector::get()->getContentScaleFactor(); - - /*auto json = res.unwrap(); - bool foundIt = false; - for (auto it = json.begin(); it != json.end(); it++) { - auto val = *it; - if (!val.getKey() || !val.getKey().has_value()) continue; - if (val.getKey().value() == gauntletName) { - foundIt = true; - auto gauntletDataOpt = matjson::Serialize::fromJson(val); - if (gauntletDataOpt.isErr()) { - log::warn("Couldn't parse the gauntlet named {}: {}", gauntletName, gauntletDataOpt.unwrapErr()); - continue; - } - auto gauntletData = gauntletDataOpt.unwrap(); - Build::create(fmt::format("{}"_spr, gauntletData.background).c_str()) - .pos(bg->getPosition()) - .scale(gauntletData.scaleBg/scaleFactor) - .zOrder(0) - .id("gauntlet-background"_spr) - .parent(node); - - if (gauntletName == "Strange") { - reverseGauntlet(node); - } - - if (gauntletData.scaleGauntlet != 0) { - // Show gauntlets sprite again - gauntlet->setVisible(true); - gauntlet->setPosition(gauntletData.bgPos); - gauntlet->setScale(gauntletData.scaleGauntlet); - gauntlet_node->setZOrder(3); - } - - // Particle - for (size_t i = 0; i < gauntletData.particles.size(); i++) { - auto particleData = gauntletData.particles.at(i); - CCParticleSystemQuad* particle = GameToolbox::particleFromString(particleData.particle, NULL, false); - node->addChild(particle); - particle->setZOrder(1); - if (particleData.pos.x == 0 && particleData.pos.y == 0) { - particle->setPosition(gauntlet->getPosition()); - } else { - particle->setPosition(particleData.pos); - } - particle->setScaleX(particleData.scale.x); - particle->setScaleY(particleData.scale.y); - particle->setID(fmt::format("gauntlet-particles-{}"_spr, i + 1)); - } - } else { - - } - } - if (!foundIt) { + auto restoreVisibility = [&]() { bg->setVisible(true); label->setVisible(true); label_shadow->setVisible(true); @@ -211,995 +205,53 @@ Thanks for their contributions! label_shadow_gauntlet->setVisible(true); gauntlet->setVisible(true); gauntlet_shadow->setVisible(true); - } - continue;*/ - - if (gauntletName == "World") { - CCSprite* sprite = CCSprite::create("world_gauntlet.png"_spr); - sprite->setPosition(bg->getPosition()); - sprite->setScale(1.270f/scaleFactor); - sprite->setZOrder(0); - sprite->setID("gauntlet-background"_spr); - node->addChild(sprite); - - // Show gauntlets sprite again - gauntlet->setVisible(true); - gauntlet->setPosition({1, 16}); - gauntlet->setScale(1); - gauntlet_node->setZOrder(3); - - // Particle - CCParticleSystemQuad* particle = GameToolbox::particleFromString("22a-1a3a0a20a90a0a29a0a0a0a0a0a0a0a0a0a7a0a0a0a0.729412a0a0.352941a0a0.796078a0a1a0a7a0a0a0a0.729412a0a0.352941a0a0.796078a0a1a0a0.4a0a0.4a0a35a0a35a0a-50a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle); - particle->setPosition({10, 24}); - particle->setZOrder(1); - particle->setScaleX(1.310f); - particle->setScaleY(2.610f); - particle->setID("gauntlet-particles-1"_spr); - CCParticleSystemQuad* particle2 = GameToolbox::particleFromString("22a-1a3a0a20a90a0a29a0a0a0a0a0a0a0a0a0a7a0a0a0a0.34902a0a0.466667a0a0.792157a0a1a0a7a0a0a0a0.34902a0a0.466667a0a0.792157a0a1a0a0.4a0a0.4a0a35a0a35a0a-50a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle2); - particle2->setPosition({6, 24}); - particle2->setZOrder(1); - particle2->setScaleX(1.310f); - particle2->setScaleY(2.610f); - particle2->setID("gauntlet-particles-2"_spr); - CCParticleSystemQuad* particle3 = GameToolbox::particleFromString("22a-1a3a0a20a90a0a29a0a0a0a0a0a0a0a0a0a7a0a0a0a0.360784a0a0.854902a0a0.368627a0a1a0a7a0a0a0a0.360784a0a0.854902a0a0.368627a0a1a0a0.4a0a0.4a0a35a0a35a0a-50a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle3); - particle3->setPosition({2, 24}); - particle3->setZOrder(1); - particle3->setScaleX(1.310f); - particle3->setScaleY(2.610f); - particle3->setID("gauntlet-particles-3"_spr); - CCParticleSystemQuad* particle4 = GameToolbox::particleFromString("22a-1a3a0a20a90a0a29a0a0a0a0a0a0a0a0a0a7a0a0a0a0.866667a0a0.839216a0a0.247059a0a1a0a7a0a0a0a0.866667a0a0.839216a0a0.247059a0a1a0a0.4a0a0.4a0a35a0a35a0a-50a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle4); - particle4->setPosition({-2, 24}); - particle4->setZOrder(1); - particle4->setScaleX(1.310f); - particle4->setScaleY(2.610f); - particle4->setID("gauntlet-particles-4"_spr); - CCParticleSystemQuad* particle5 = GameToolbox::particleFromString("22a-1a3a0a20a90a0a29a0a0a0a0a0a0a0a0a0a7a0a0a0a0.866667a0a0.294118a0a0.247059a0a1a0a7a0a0a0a0.866667a0a0.294118a0a0.247059a0a1a0a0.4a0a0.4a0a35a0a35a0a-50a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle5); - particle5->setPosition({-6, 24}); - particle5->setZOrder(1); - particle5->setScaleX(1.310f); - particle5->setScaleY(2.610f); - particle5->setID("gauntlet-particles-5"_spr); - CCParticleSystemQuad* particle6 = GameToolbox::particleFromString("22a-1a3a0a20a90a0a29a0a0a0a0a0a0a0a0a0a7a0a0a0a0.729412a0a0.352941a0a0.796078a0a1a0a7a0a0a0a0.729412a0a0.352941a0a0.796078a0a1a0a0.4a0a0.4a0a35a0a35a0a-50a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle6); - particle6->setPosition({-9, 24}); - particle6->setZOrder(1); - particle6->setScaleX(-1.310f); - particle6->setScaleY(2.610f); - particle6->setID("gauntlet-particles-6"_spr); - CCParticleSystemQuad* particle7 = GameToolbox::particleFromString("22a-1a3a0a20a90a0a29a0a0a0a0a0a0a0a0a0a7a0a0a0a0.34902a0a0.466667a0a0.792157a0a1a0a7a0a0a0a0.34902a0a0.466667a0a0.792157a0a1a0a0.4a0a0.4a0a35a0a35a0a-50a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle7); - particle7->setPosition({-5, 24}); - particle7->setZOrder(1); - particle7->setScaleX(-1.310f); - particle7->setScaleY(2.610f); - particle7->setID("gauntlet-particles-7"_spr); - CCParticleSystemQuad* particle8 = GameToolbox::particleFromString("22a-1a3a0a20a90a0a29a0a0a0a0a0a0a0a0a0a7a0a0a0a0.360784a0a0.854902a0a0.368627a0a1a0a7a0a0a0a0.360784a0a0.854902a0a0.368627a0a1a0a0.4a0a0.4a0a35a0a35a0a-50a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle8); - particle8->setPosition({-1, 24}); - particle8->setZOrder(1); - particle8->setScaleX(-1.310f); - particle8->setScaleY(2.610f); - particle8->setID("gauntlet-particles-8"_spr); - CCParticleSystemQuad* particle9 = GameToolbox::particleFromString("22a-1a3a0a20a90a0a29a0a0a0a0a0a0a0a0a0a7a0a0a0a0.866667a0a0.839216a0a0.247059a0a1a0a7a0a0a0a0.866667a0a0.839216a0a0.247059a0a1a0a0.4a0a0.4a0a35a0a35a0a-50a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle9); - particle9->setPosition({3, 24}); - particle9->setZOrder(1); - particle9->setScaleX(-1.310f); - particle9->setScaleY(2.610f); - particle9->setID("gauntlet-particles-9"_spr); - CCParticleSystemQuad* particle10 = GameToolbox::particleFromString("22a-1a3a0a20a90a0a29a0a0a0a0a0a0a0a0a0a7a0a0a0a0.866667a0a0.294118a0a0.247059a0a1a0a7a0a0a0a0.866667a0a0.294118a0a0.247059a0a1a0a0.4a0a0.4a0a35a0a35a0a-50a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle10); - particle10->setPosition({7, 24}); - particle10->setZOrder(1); - particle10->setScaleX(-1.310f); - particle10->setScaleY(2.610f); - particle10->setID("gauntlet-particles-10"_spr); - CCParticleSystemQuad* particle11 = GameToolbox::particleFromString("38a-1a1.5a0.3a21a90a90a0a0a31a69a0a0a0a0a0a0a2a1a65a57a1a0a1a0a1a0a1a0a5a1a0a0a1a0a1a0a1a0a1a0a0.2a0a0.4a0a0a0a0a0a0a0a0a2a1a0a0a0a169a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle11); - particle11->setPosition({1, 6}); - particle11->setZOrder(1); - particle11->setScaleX(1.710f); - particle11->setScaleY(1.660f); - particle11->setID("gauntlet-particles-11"_spr); - CCParticleSystemQuad* particle12 = GameToolbox::particleFromString("11a-1a1.5a0.3a6a90a90a0a0a31a69a0a0a0a0a0a0a4a1a0a0a1a0a1a0a1a0a1a0a7a1a52a55a1a0a1a0a1a0a1a0a0.2a0a0.4a0a0a0a0a0a0a0a0a2a1a0a0a0a167a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle12); - particle12->setPosition({1, 16}); - particle12->setZOrder(1); - particle12->setScaleX(1.835f); - particle12->setScaleY(1.485f); - particle12->setID("gauntlet-particles-12"_spr); - } else if (gauntletName == "Fantasy") { - CCSprite* sprite = CCSprite::create("fantasy_gauntlet.png"_spr); - sprite->setPosition(bg->getPosition()); - sprite->setScale(1.225f/scaleFactor); - sprite->setZOrder(0); - sprite->setID("gauntlet-background"_spr); - node->addChild(sprite); - - // Show gauntlets sprite again - gauntlet->setVisible(true); - gauntlet->setPosition({-2, 14}); - gauntlet->setScale(0.925f); - gauntlet_node->setZOrder(2); - - // Particle - CCParticleSystemQuad* particle = GameToolbox::particleFromString("26a-1a5a0.3a4a-90a14a9a0a17a0a0a0a0a0a0a0a2a1a76a325a0.537255a0a0.898039a0a0.164706a0a1a0a2a1a148a149a0.537255a0a0.898039a0a0.164706a0a1a0a0a0a0.3a0a0a0a0a0a0a0a0a2a0a0a0a0a146a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle); - particle->setPosition({gauntlet->getPositionX(), 103}); - particle->setZOrder(1); - particle->setScaleX(2.880f); - particle->setScaleY(4.603f); - particle->setID("gauntlet-particles-1"_spr); - } else if (gauntletName == "Portal") { - CCSprite* sprite = CCSprite::create("portal_gauntlet.png"_spr); - sprite->setPosition(bg->getPosition()); - sprite->setScale(1.225f/scaleFactor); - sprite->setZOrder(0); - sprite->setID("gauntlet-background"_spr); - node->addChild(sprite); - - // Show gauntlets sprite again - gauntlet->setVisible(true); - gauntlet->setPosition({-2, 14}); - gauntlet->setScale(0.925f); - gauntlet_node->setZOrder(2); - - // Particle - CCParticleSystemQuad* particle = GameToolbox::particleFromString("13a-1a1a0.3a10a90a90a29a0a11a0a0a0a0a0a0a0a50a1a0a37a0.376471a0a0.109804a0a0.243137a0a1a0a32a1a142a96a0.376471a0a0.109804a0a0.243137a0a1a0a0.5a0a0.3a0a0a0a0a0a0a0a1a2a1a0a0a0a190a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle); - particle->setPosition({gauntlet->getPositionX(), 14}); - particle->setZOrder(1); - particle->setScaleX(2.880f); - particle->setScaleY(3.978f); - particle->setID("gauntlet-particles-1"_spr); - CCParticleSystemQuad* particle2 = GameToolbox::particleFromString("13a-1a1a0.3a10a90a90a29a0a11a0a0a0a0a0a0a0a50a1a0a37a0.0705882a0a0.172549a0a0.203922a0a1a0a32a1a142a96a0.0705882a0a0.172549a0a0.203922a0a1a0a0.5a0a0.3a0a0a0a0a0a0a0a1a2a1a0a0a0a190a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle2); - particle2->setPosition({gauntlet->getPositionX(), 14}); - particle2->setZOrder(1); - particle2->setScaleX(2.880f); - particle2->setScaleY(3.978f); - particle2->setID("gauntlet-particles-2"_spr); - CCParticleSystemQuad* particle3 = GameToolbox::particleFromString("38a-1a2a0.3a16a90a180a29a0a11a0a0a0a0a0a0a0a6a1a0a0a0.760784a0a0.380392a0a0.835294a0a1a0a3a1a0a0a0.760784a0a0.380392a0a0.835294a0a1a0a0.18a0a0.49a0a43a0a33a0a-34a0a1a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle3); - particle3->setPosition({gauntlet->getPositionX(), 14}); - particle3->setZOrder(1); - particle3->setScaleX(1.030f); - particle3->setScaleY(1.030f); - particle3->setID("gauntlet-particles-3"_spr); - CCParticleSystemQuad* particle4 = GameToolbox::particleFromString("38a-1a2a0.3a16a90a180a29a0a11a0a0a0a0a0a0a0a6a1a0a0a0.376471a0a0.690196a0a0.831373a0a1a0a3a1a0a0a0.376471a0a0.690196a0a0.831373a0a1a0a0.18a0a0.49a0a43a0a33a0a-34a0a1a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle4); - particle4->setPosition({gauntlet->getPositionX(), 14}); - particle4->setZOrder(1); - particle4->setScaleX(1.155f); - particle4->setScaleY(1.355f); - particle4->setID("gauntlet-particles-4"_spr); - CCParticleSystemQuad* particle5 = GameToolbox::particleFromString("30a-1a1a0.3a30a90a180a29a0a11a0a0a0a0a0a0a0a4a1a0a0a0.760784a0a0.380392a0a0.835294a0a1a0a2a1a0a0a0.760784a0a0.380392a0a0.835294a0a1a0a0.18a0a0.49a0a43a0a33a0a-41a0a1a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle5); - particle5->setPosition({gauntlet->getPositionX(), 14}); - particle5->setZOrder(1); - particle5->setScaleX(1.455f); - particle5->setScaleY(1.655f); - particle5->setID("gauntlet-particles-5"_spr); - CCParticleSystemQuad* particle6 = GameToolbox::particleFromString("30a-1a1a0.3a30a90a180a29a0a11a0a0a0a0a0a0a0a4a1a0a0a0.376471a0a0.690196a0a0.831373a0a1a0a2a1a0a0a0.376471a0a0.690196a0a0.831373a0a1a0a0.18a0a0.49a0a43a0a33a0a-41a0a1a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle6); - particle6->setPosition({gauntlet->getPositionX(), 14}); - particle6->setZOrder(1); - particle6->setScaleX(1.355f); - particle6->setScaleY(1.855f); - particle6->setID("gauntlet-particles-6"_spr); - } else if (gauntletName == "Demon") { - CCSprite* sprite = CCSprite::create("demon_gauntlet.png"_spr); - sprite->setPosition(bg->getPosition()); - sprite->setScale(1.275f/scaleFactor); - sprite->setZOrder(0); - sprite->setID("gauntlet-background"_spr); - node->addChild(sprite); - - // Show gauntlets sprite again - gauntlet->setVisible(true); - gauntlet->setPosition({-1, 22}); - gauntlet->setScale(1.1f); - gauntlet_node->setZOrder(2); - - // Particle - CCParticleSystemQuad* particle = GameToolbox::particleFromString("66a-1a3a0.3a20a90a27a11a0a37a38a0a0a0a0a0a0a5a1a0a0a0.694118a0a0.45098a0a0.054902a0a1a0a2a1a0a0a0.509804a0a0.172549a0a0a0a1a0a0.5a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle); - particle->setPosition(gauntlet->getPosition()); - particle->setZOrder(1); - particle->setID("gauntlet-particles-1"_spr); - CCParticleSystemQuad* particle2 = GameToolbox::particleFromString("29a-1a3a0.3a8a90a27a11a0a37a38a0a0a0a0a0a0a50a1a0a0a0.0509804a0a0.0392157a0a0.0352941a0a1a0a50a1a0a0a0.0509804a0a0.0392157a0a0.0352941a0a1a0a0.5a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle2); - particle2->setPosition(gauntlet->getPosition()); - particle2->setZOrder(1); - particle2->setID("gauntlet-particles-2"_spr); - CCParticleSystemQuad* particle3 = GameToolbox::particleFromString("31a-1a1a0.3a23a90a90a29a0a11a0a0a0a0a0a0a0a33a1a89a149a0.454902a0a0.321569a0a0.152941a0a1a0a50a1a145a68a0.454902a0a0.321569a0a0.152941a0a1a0a0.5a0a0.5a0a0a0a0a0a0a0a1a2a1a0a0a0a192a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle3); - particle3->setPosition(gauntlet->getPosition()); - particle3->setZOrder(1); - particle3->setID("gauntlet-particles-3"_spr); - CCParticleSystemQuad* particle4 = GameToolbox::particleFromString("30a-1a2a0.3a13a90a8a21a0a8a0a0a45a0a0a0a0a40a1a89a149a0.396078a0a0.27451a0a0.14902a0a1a0a24a1a145a68a0.141176a0a0.0352941a0a0.0313726a0a1a0a0.5a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle4); - particle4->setPosition(gauntlet->getPosition()); - particle4->setZOrder(1); - particle4->setID("gauntlet-particles-4"_spr); - CCParticleSystemQuad* particle5 = GameToolbox::particleFromString("30a-1a1a0.3a13a90a8a21a0a0a0a0a45a0a0a0a0a21a1a89a149a0.894118a0a0.631373a0a0.345098a0a1a0a6a1a145a68a0.894118a0a0.631373a0a0.345098a0a1a0a0.5a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a183a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle5); - particle5->setPosition(gauntlet->getPosition()); - particle5->setZOrder(1); - particle5->setID("gauntlet-particles-5"_spr); - } else if (gauntletName == "Castle") { - CCSprite* sprite = CCSprite::create("castle_gauntlet.png"_spr); - sprite->setPosition(bg->getPosition()); - sprite->setScale(1.275f/scaleFactor); - sprite->setZOrder(0); - sprite->setID("gauntlet-background"_spr); - node->addChild(sprite); - } else if (gauntletName == "Galaxy") { - CCSprite* sprite = CCSprite::create("galaxy_gauntlet.png"_spr); - sprite->setPosition(bg->getPosition()); - sprite->setScale(1.695f/scaleFactor); - sprite->setZOrder(0); - sprite->setID("gauntlet-background"_spr); - node->addChild(sprite); - - // Show gauntlets sprite again - gauntlet->setVisible(true); - gauntlet->setPosition({0, 11}); - gauntlet->setScale(0.975); - gauntlet_node->setZOrder(3); - - // Particle - CCParticleSystemQuad* particle = GameToolbox::particleFromString("11a-1a1a0.3a8a90a90a0a0a0a0a0a0a0a0a0a0a50a3a0a0a0.627451a0a0.286275a0a0.470588a0a1a0a50a3a106a70a0.627451a0a0.286275a0a0.470588a0a1a0a0a0a0a0a0a0a0a0a0a0a0a2a1a0a0a0a176a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle); - particle->setPosition({0, 24}); - particle->setZOrder(1); - particle->setScaleX(1.610f); - particle->setScaleY(1.610f); - particle->setID("gauntlet-particles-1"_spr); - CCParticleSystemQuad* particle2 = GameToolbox::particleFromString("14a-1a1.2a0.3a9a112a6a42a0a15a0a0a0a0a0a0a0a4a0a340a0a0.913725a0a0.72549a0a0.337255a0a1a0a4a0a340a0a0.913725a0a0.72549a0a0.337255a0a1a0a0.3a0a0.3a0a0a0a0a0a0a0a0a2a1a0a0a0a31a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle2); - particle2->setPosition({-25, 111}); - particle2->setZOrder(1); - particle2->setScaleX(2.660f); - particle2->setScaleY(3.710f); - particle2->setRotation(180); - particle2->setID("gauntlet-particles-2"_spr); - CCParticleSystemQuad* particle3 = GameToolbox::particleFromString("38a-1a1.5a0.3a21a90a90a0a0a31a69a0a0a0a0a0a0a2a1a65a57a0.886275a0a0.866667a0a0.454902a0a1a0a5a1a0a0a0.886275a0a0.866667a0a0.454902a0a1a0a0.2a0a0.4a0a0a0a0a0a0a0a0a2a1a0a0a0a169a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle3); - particle3->setPosition({2, 6}); - particle3->setZOrder(1); - particle3->setScaleX(1.685f); - particle3->setScaleY(1.485f); - particle3->setID("gauntlet-particles-3"_spr); - CCParticleSystemQuad* particle4 = GameToolbox::particleFromString("11a-1a1.5a0.3a6a90a90a0a0a31a69a0a0a0a0a0a0a4a1a0a0a0.890196a0a0.376471a0a0.65098a0a1a0a7a1a52a55a0.890196a0a0.376471a0a0.65098a0a1a0a0.2a0a0.4a0a0a0a0a0a0a0a0a2a1a0a0a0a167a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle4); - particle4->setPosition({2, -2}); - particle4->setZOrder(1); - particle4->setScaleX(1.310f); - particle4->setScaleY(1.635f); - particle4->setID("gauntlet-particles-4"_spr); - CCParticleSystemQuad* particle5 = GameToolbox::particleFromString("4a-1a1a0.3a3a90a90a0a0a0a0a0a0a0a0a0a0a50a3a0a0a0.862745a0a0.352941a0a0.623529a0a1a0a50a3a106a22a0.862745a0a0.352941a0a0.623529a0a1a0a0.3a0a0.3a0a0a0a0a0a0a0a0a2a1a0a0a0a164a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle5); - particle5->setPosition({0, 24}); - particle5->setZOrder(1); - particle5->setScaleX(1.785f); - particle5->setScaleY(1.785f); - particle5->setID("gauntlet-particles-5"_spr); - } else if (gauntletName == "Universe") { - CCSprite* sprite = CCSprite::create("universe_gauntlet.png"_spr); - sprite->setPosition(bg->getPosition()); - sprite->setScale(1.270f/scaleFactor); - sprite->setZOrder(0); - sprite->setID("gauntlet-background"_spr); - node->addChild(sprite); - - // Show gauntlets sprite again - gauntlet->setVisible(true); - gauntlet->setPosition({0, 16}); - gauntlet->setScale(0.975f); - gauntlet_node->setZOrder(3); + }; - // Particle - CCParticleSystemQuad* particle = GameToolbox::particleFromString("23a-1a3a0.3a6a90a180a29a0a11a0a0a0a0a0a0a0a38a1a0a0a0.0627451a0a0.0980392a0a0.105882a0a1a0a50a1a0a0a0.0627451a0a0.0980392a0a0.105882a0a1a0a0.1a0a0.5a0a10a0a28a0a0a0a1a2a1a0a1a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle); - particle->setPosition({0, 11}); - particle->setZOrder(1); - particle->setScaleX(1.485f); - particle->setScaleY(2.685f); - particle->setID("gauntlet-particles-1"_spr); - CCParticleSystemQuad* particle2 = GameToolbox::particleFromString("20a-1a0.5a0a15a90a0a44a0a11a0a0a0a0a0a0a0a4a1a0a0a0.760784a0a0.447059a0a0.94902a0a1a0a4a1a0a0a0.760784a0a0.447059a0a0.94902a0a1a0a0.1a0a0.1a0a0a0a0a0a0a0a0a2a1a0a0a0a33a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle2); - particle2->setPosition({32, -74}); - particle2->setZOrder(1); - particle2->setScaleX(3.685f); - particle2->setScaleY(8.310f); - particle2->setRotation(-20); - particle2->setID("gauntlet-particles-2"_spr); - CCParticleSystemQuad* particle3 = GameToolbox::particleFromString("19a-1a0.5a0a38a90a0a44a0a20a0a0a0a0a0a0a0a2a1a0a0a0.94902a0a0.67451a0a0.447059a0a1a0a2a1a0a0a0.94902a0a0.67451a0a0.447059a0a1a0a0.1a0a0.1a0a0a0a0a0a0a0a0a2a1a0a0a0a33a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle3); - particle3->setPosition({34, -96}); - particle3->setZOrder(1); - particle3->setScaleX(2.135f); - particle3->setScaleY(8.935f); - particle3->setRotation(-20); - particle3->setID("gauntlet-particles-3"_spr); - CCParticleSystemQuad* particle4 = GameToolbox::particleFromString("20a-1a1a0a15a90a0a36a0a11a0a0a0a0a0a0a0a4a1a0a0a0.447059a0a0.654902a0a0.94902a0a1a0a4a1a0a0a0.447059a0a0.654902a0a0.94902a0a1a0a0.2a0a0.2a0a0a0a0a0a0a0a0a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle4); - particle4->setPosition({41, -92}); - particle4->setZOrder(1); - particle4->setScaleX(3.235f); - particle4->setScaleY(6.585f); - particle4->setRotation(-20); - particle4->setID("gauntlet-particles-4"_spr); - CCParticleSystemQuad* particle5 = GameToolbox::particleFromString("11a-1a1a0.3a8a90a90a0a0a0a0a0a0a0a0a0a0a50a3a0a0a0.239216a0a0.403922a0a0.419608a0a1a0a50a3a106a70a0.239216a0a0.403922a0a0.419608a0a1a0a0a0a0a0a0a0a0a0a0a0a0a2a1a0a0a0a176a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle5); - particle5->setPosition({0, 24}); - particle5->setZOrder(1); - particle5->setScaleX(1.710f); - particle5->setScaleY(1.710f); - particle5->setID("gauntlet-particles-5"_spr); - CCParticleSystemQuad* particle6 = GameToolbox::particleFromString("4a-1a1a0.3a3a90a90a0a0a0a0a0a0a0a0a0a0a50a3a0a0a0.32549a0a0.537255a0a0.560784a0a1a0a50a3a106a22a0.32549a0a0.537255a0a0.560784a0a1a0a0.3a0a0.3a0a0a0a0a0a0a0a0a2a1a0a0a0a190a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle6); - particle6->setPosition({-2, 24}); - particle6->setZOrder(1); - particle6->setScaleX(2.710f); - particle6->setScaleY(2.710f); - particle6->setID("gauntlet-particles-6"_spr); - } else if (gauntletName == "Cursed") { - CCSprite* sprite = CCSprite::create("cursed_gauntlet.png"_spr); - sprite->setPosition(bg->getPosition()); - sprite->setScale(1.270f/scaleFactor); - sprite->setZOrder(0); - sprite->setID("gauntlet-background"_spr); - node->addChild(sprite); - - // Show gauntlets sprite again - gauntlet->setVisible(true); - gauntlet->setPosition({1, 16}); - gauntlet->setScale(1); - gauntlet_node->setZOrder(3); - - // Particle - CCParticleSystemQuad* particle = GameToolbox::particleFromString("5a-1a1a0.3a3a90a90a29a0a11a0a0a0a0a0a0a0a50a1a24a45a0.105882a0a0.380392a0a0.105882a0a1a0a44a1a48a28a0.105882a0a0.380392a0a0.105882a0a1a0a0a0a0a0a0a0a0a0a0a0a1a2a1a0a0a0a175a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle); - particle->setPosition(gauntlet->getPosition()); - particle->setZOrder(1); - particle->setScaleX(2.035f); - particle->setScaleY(2.035f); - particle->setID("gauntlet-particles-1"_spr); - CCParticleSystemQuad* particle2 = GameToolbox::particleFromString("5a-1a1a0.3a3a90a90a29a0a11a0a0a0a0a0a0a0a50a1a24a45a0.105882a0a0.380392a0a0.105882a0a1a0a44a1a48a28a0.105882a0a0.380392a0a0.105882a0a1a0a0a0a0a0a0a0a0a0a0a0a1a2a1a0a0a0a175a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle2); - particle2->setPosition(gauntlet->getPosition()); - particle2->setZOrder(1); - particle2->setScaleX(2.035f); - particle2->setScaleY(2.035f); - particle2->setID("gauntlet-particles-2"_spr); - CCParticleSystemQuad* particle3 = GameToolbox::particleFromString("93a-1a1a0.3a71a90a0a52a0a10a0a23a0a0a0a0a0a2a1a0a0a0.00392157a0a0.905882a0a0a0a1a0a2a1a30a0a0.00392157a0a0.905882a0a0a0a1a0a0.3a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a32a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle3); - particle3->setPosition({17, 94}); - particle3->setZOrder(1); - particle3->setScaleX(4.510f); - particle3->setScaleY(3.335f); - particle3->setRotation(180); - particle3->setID("gauntlet-particles-3"_spr); - } else if (gauntletName == "Doom") { - CCSprite* sprite = CCSprite::create("doom_gauntlet.png"_spr); - sprite->setPosition(bg->getPosition()); - sprite->setScale(1.270f/scaleFactor); - sprite->setZOrder(0); - sprite->setID("gauntlet-background"_spr); - node->addChild(sprite); - - // Show gauntlets sprite again - gauntlet->setVisible(true); - gauntlet->setPosition({0, 19}); - gauntlet->setScale(1.025f); - gauntlet_node->setZOrder(3); - - // Particle - CCParticleSystemQuad* particle = GameToolbox::particleFromString("66a-1a3a0.3a20a90a27a11a0a43a83a0a0a0a0a0a0a5a1a0a0a0.733333a0a0.607843a0a0.12549a0a1a0a3a1a0a0a0.733333a0a0.470588a0a0.12549a0a1a0a0.5a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle); - particle->setPosition({0, -7}); - particle->setZOrder(1); - particle->setScaleX(1.235f); - particle->setScaleY(1.235f); - particle->setID("gauntlet-particles-1"_spr); - CCParticleSystemQuad* particle2 = GameToolbox::particleFromString("44a-1a3a0.3a13a90a27a11a0a32a62a0a0a0a0a0a0a50a1a0a0a0.0666667a0a0.0509804a0a0.0470588a0a1a0a50a1a0a0a0.0666667a0a0.0509804a0a0.0470588a0a1a0a0.5a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle2); - particle2->setPosition({0, -13}); - particle2->setZOrder(1); - particle2->setScaleX(1.310f); - particle2->setScaleY(1.435f); - particle2->setID("gauntlet-particles-2"_spr); - CCParticleSystemQuad* particle3 = GameToolbox::particleFromString("6a-1a1.9a0a3a90a17a69a0a3a0a0a-102a0a0a0a0a4a1a57a105a1a0a1a0a1a0a1a0a4a1a0a0a1a0a1a0a1a0a1a0a0a0a0a0a0a0a0a0a0a0a0a2a1a0a0a0a145a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle3); - particle3->setPosition(gauntlet->getPosition()); - particle3->setZOrder(1); - particle3->setScaleX(2.035f); - particle3->setScaleY(2.035f); - particle3->setID("gauntlet-particles-3"_spr); - } else if (gauntletName == "Split") { - CCSprite* sprite = CCSprite::create("split_gauntlet.png"_spr); - sprite->setPosition(bg->getPosition()); - sprite->setScale(1.270f/scaleFactor); - sprite->setZOrder(0); - sprite->setID("gauntlet-background"_spr); - node->addChild(sprite); - - // Show gauntlets sprite again - gauntlet->setVisible(true); - gauntlet->setPosition({-1, 20}); - gauntlet->setScale(0.975f); - gauntlet_node->setZOrder(2); - - // Particle - CCParticleSystemQuad* particle = GameToolbox::particleFromString("48a-1a2a0.3a20a90a2a19a0a10a20a0a0a0a0a0a0a4a1a0a0a0.666667a0a0.360784a0a0.603922a0a1a0a2a1a0a0a0.666667a0a0.360784a0a0.603922a0a1a0a0.5a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a31a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle); - particle->setPosition({-28, -65}); - particle->setZOrder(1); - particle->setScaleX(2.180f); - particle->setScaleY(4.003f); - particle->setID("gauntlet-particles-1"_spr); - CCParticleSystemQuad* particle2 = GameToolbox::particleFromString("48a-1a2a0.3a20a90a2a19a0a10a20a0a0a0a0a0a0a4a1a0a0a0.454902a0a0.215686a0a0.403922a0a1a0a2a1a0a0a0.454902a0a0.215686a0a0.403922a0a1a0a0.5a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle2); - particle2->setPosition({-28, -65}); - particle2->setZOrder(1); - particle2->setScaleX(2.180f); - particle2->setScaleY(4.003f); - particle2->setID("gauntlet-particles-2"_spr); - CCParticleSystemQuad* particle3 = GameToolbox::particleFromString("48a-1a2a0.3a20a90a2a19a0a10a20a0a0a0a0a0a0a4a1a0a0a0.243137a0a0.329412a0a0.341176a0a1a0a2a1a0a0a0.243137a0a0.329412a0a0.341176a0a1a0a0.5a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a31a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle3); - particle3->setPosition({25, 65}); - particle3->setZOrder(1); - particle3->setScaleX(2.180f); - particle3->setScaleY(4.003f); - particle3->setRotation(180); - particle3->setID("gauntlet-particles-3"_spr); - CCParticleSystemQuad* particle4 = GameToolbox::particleFromString("48a-1a2a0.3a20a90a2a19a0a10a20a0a0a0a0a0a0a4a1a0a0a0.145098a0a0.203922a0a0.207843a0a1a0a2a1a0a0a0.145098a0a0.203922a0a0.207843a0a1a0a0.5a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle4); - particle4->setPosition({25, 65}); - particle4->setZOrder(1); - particle4->setScaleX(2.180f); - particle4->setScaleY(4.003f); - particle4->setRotation(180); - particle4->setID("gauntlet-particles-4"_spr); - CCParticleSystemQuad* particle5 = GameToolbox::particleFromString("30a-1a2a0.3a13a90a90a29a0a11a0a0a0a0a0a0a0a6a1a0a0a0.705882a0a0.34902a0a0.698039a0a1a0a2a1a0a0a0.584314a0a0.34902a0a0.705882a0a1a0a0a0a0.5a0a6a0a34a0a0a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle5); - particle5->setPosition({0, 29}); - particle5->setZOrder(1); - particle5->setScaleX(1.955f); - particle5->setScaleY(1.955f); - particle5->setRotation(90); - particle5->setID("gauntlet-particles-5"_spr); - CCParticleSystemQuad* particle6 = GameToolbox::particleFromString("30a-1a2a0.3a13a90a90a29a0a11a0a0a0a0a0a0a0a6a1a0a0a0.254902a0a0.333333a0a0.388235a0a1a0a2a1a0a0a0.254902a0a0.333333a0a0.388235a0a1a0a0a0a0.5a0a6a0a34a0a0a0a1a2a1a0a0a0a159a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle6); - particle6->setPosition({0, 29}); - particle6->setZOrder(1); - particle6->setScaleX(1.955f); - particle6->setScaleY(1.955f); - particle6->setRotation(-90); - particle6->setID("gauntlet-particles-6"_spr); - CCParticleSystemQuad* particle7 = GameToolbox::particleFromString("30a-1a2a0.3a13a90a90a29a0a11a0a0a0a0a0a0a0a6a1a0a0a0.705882a0a0.34902a0a0.698039a0a1a0a2a1a0a0a0.584314a0a0.34902a0a0.705882a0a1a0a0a0a0.5a0a6a0a34a0a0a0a1a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle7); - particle7->setPosition({0, 29}); - particle7->setZOrder(1); - particle7->setScaleX(1.955f); - particle7->setScaleY(1.955f); - particle7->setRotation(90); - particle7->setID("gauntlet-particles-7"_spr); - CCParticleSystemQuad* particle8 = GameToolbox::particleFromString("30a-1a2a0.3a13a90a90a29a0a11a0a0a0a0a0a0a0a6a1a0a0a0.254902a0a0.333333a0a0.388235a0a1a0a2a1a0a0a0.254902a0a0.333333a0a0.388235a0a1a0a0a0a0.5a0a6a0a34a0a0a0a1a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle8); - particle8->setPosition({0, 29}); - particle8->setZOrder(1); - particle8->setScaleX(1.955f); - particle8->setScaleY(1.955f); - particle8->setRotation(-90); - particle8->setID("gauntlet-particles-8"_spr); - } else if (gauntletName == "Monster") { - CCSprite* sprite = CCSprite::create("monster_gauntlet.png"_spr); - sprite->setPosition(bg->getPosition()); - sprite->setScale(1.270f/scaleFactor); - sprite->setZOrder(0); - sprite->setID("gauntlet-background"_spr); - node->addChild(sprite); - - // Show gauntlets sprite again - gauntlet->setVisible(true); - gauntlet->setPosition({0, 22}); - gauntlet->setScale(1.075f); - gauntlet_node->setZOrder(2); - - // Particle - CCParticleSystemQuad* particle = GameToolbox::particleFromString("30a-1a3a0.3a9a90a9a11a0a17a40a0a0a0a0a0a0a4a1a0a0a0.0509804a0a0.929412a0a0a0a1a0a2a1a0a0a0.360784a0a0.486275a0a0.352941a0a1a0a0.1a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a165a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle); - particle->setPosition({gauntlet->getPositionX(), -37}); - particle->setZOrder(1); - particle->setScaleX(2.510f); - particle->setScaleY(1.985f); - particle->setID("gauntlet-particles-1"_spr); - CCParticleSystemQuad* particle2 = GameToolbox::particleFromString("66a-1a3a0.3a20a90a27a11a0a37a38a0a0a0a0a0a0a5a1a0a0a0.0156863a0a0.866667a0a0.027451a0a1a0a3a1a0a0a0.164706a0a0.509804a0a0a0a1a0a0.5a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle2); - particle2->setPosition({gauntlet->getPositionX(), -38}); - particle2->setZOrder(1); - particle2->setScaleX(1.072f); - particle2->setScaleY(2.475f); - particle2->setID("gauntlet-particles-2"_spr); - CCParticleSystemQuad* particle3 = GameToolbox::particleFromString("18a-1a3a0.3a5a90a9a11a0a32a28a0a0a0a0a0a0a50a1a0a0a0.0352941a0a0.0901961a0a0.0313726a0a1a0a50a1a0a0a0.0352941a0a0.0901961a0a0.0313726a0a1a0a1a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle3); - particle3->setPosition({gauntlet->getPositionX(), -48}); - particle3->setZOrder(1); - particle3->setScaleX(1.470f); - particle3->setScaleY(1.957f); - particle3->setID("gauntlet-particles-3"_spr); - } else if (gauntletName == "Mystery") { - CCSprite* sprite = CCSprite::create("mystery_gauntlet.png"_spr); - sprite->setPosition(bg->getPosition()); - sprite->setScale(1.270f/scaleFactor); - sprite->setZOrder(0); - sprite->setID("gauntlet-background"_spr); - node->addChild(sprite); - - // Show gauntlets sprite again - gauntlet->setVisible(true); - gauntlet->setPosition({3, 13}); - gauntlet->setScale(0.975f); - gauntlet_node->setZOrder(3); - - // Particle - CCParticleSystemQuad* particle = GameToolbox::particleFromString("30a-1a3a0.3a9a90a180a29a0a11a0a0a0a0a0a0a0a2a1a0a0a1a0a0.8a0a0.258824a0a1a0a8a1a0a0a1a0a0.8a0a0.258824a0a1a0a0.5a0a0.5a0a6a0a30a0a0a0a1a2a0a0a0a0a11a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle); - particle->setPosition(gauntlet->getPosition()); - particle->setZOrder(2); - particle->setScaleX(2.035f); - particle->setScaleY(2.035f); - particle->setID("gauntlet-particles-1"_spr); - CCParticleSystemQuad* particle2 = GameToolbox::particleFromString("31a-1a3a0.3a9a90a180a0a0a29a82a0a0a0a0a0a0a4a1a0a0a0.388235a0a0.741176a0a1a0a1a0a15a1a0a0a0.388235a0a0.741176a0a1a0a1a0a1a0a1a0a6a0a30a0a0a0a0a2a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle2); - particle2->setPosition({0, 0}); - particle2->setZOrder(1); - particle2->setScaleX(1.435f); - particle2->setScaleY(1.435f); - particle2->setID("gauntlet-particles-2"_spr); - } else if (gauntletName == "Strange") { - CCSprite* sprite = CCSprite::create("strange_gauntlet.png"_spr); - sprite->setPosition(bg->getPosition()); - sprite->setScale(1.270f/scaleFactor); - sprite->setZOrder(0); - sprite->setID("gauntlet-background"_spr); - node->addChild(sprite); - - reverseGauntlet(node); - - // Show gauntlets sprite again - gauntlet->setVisible(true); - gauntlet->setPosition({2, -18}); - gauntlet->setRotation(180); - gauntlet->setScale(0.975f); - gauntlet->setFlipX(true); - gauntlet_node->setZOrder(2); - - // Particle - CCParticleSystemQuad* particle = GameToolbox::particleFromString("36a-1a3a0.3a6a-90a24a9a0a11a0a0a-7a0a0a0a0a2a1a76a325a0.592157a0a0.294118a0a0.658824a0a1a0a2a1a201a149a0.592157a0a0.294118a0a0.658824a0a1a0a0.3a0a0.3a0a0a0a0a0a0a0a0a2a0a0a0a0a96a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle); - particle->setPosition({2, -65}); - particle->setZOrder(2); - particle->setScaleX(2.480f); - particle->setScaleY(2.728f); - particle->setRotation(180); - particle->setID("gauntlet-particles-1"_spr); - CCParticleSystemQuad* particle2 = GameToolbox::particleFromString("17a-1a4a0.3a3a180a0a8a0a47a34a0a0a0a0a0a0a50a1a0a0a0.243137a0a0.239216a0a0.239216a0a1a0a50a1a0a0a0.454902a0a0.447059a0a0.447059a0a1a0a1a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle2); - particle2->setPosition({17, 60}); - particle2->setZOrder(3); - particle2->setScaleX(0.805f); - particle2->setScaleY(1.155f); - particle2->setID("gauntlet-particles-2"_spr); - } else if (gauntletName == "Water") { - CCSprite* sprite = CCSprite::create("water_gauntlet.png"_spr); - sprite->setPosition(bg->getPosition()); - sprite->setScale(1.270f/scaleFactor); - sprite->setZOrder(0); - sprite->setID("gauntlet-background"_spr); - node->addChild(sprite); - } else if (gauntletName == "Fire") { - CCSprite* sprite = CCSprite::create("fire_gauntlet.png"_spr); - sprite->setPosition(bg->getPosition()); - sprite->setScale(1.270f/scaleFactor); - sprite->setZOrder(0); - sprite->setID("gauntlet-background"_spr); - node->addChild(sprite); - - // Show gauntlets sprite again - gauntlet->setVisible(true); - gauntlet->setPosition({0, 15}); - gauntlet->setScale(1.050f); - gauntlet_node->setZOrder(2); - - // Particle - CCParticleSystemQuad* particle = GameToolbox::particleFromString("30a-1a2a0.3a13a90a8a21a0a8a0a0a45a0a0a0a0a50a1a89a149a0.396078a0a0.27451a0a0.14902a0a1a0a31a1a145a68a0.141176a0a0.0352941a0a0.0313726a0a1a0a0.5a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle); - particle->setPosition({-1, 0}); - particle->setZOrder(1); - particle->setScaleX(1.435f); - particle->setScaleY(0.935f); - particle->setID("gauntlet-particles-1"_spr); - CCParticleSystemQuad* particle2 = GameToolbox::particleFromString("30a-1a1a0.3a13a90a8a21a0a0a0a0a45a0a0a0a0a21a1a89a149a0.894118a0a0.631373a0a0.345098a0a1a0a13a1a145a68a0.894118a0a0.631373a0a0.345098a0a1a0a0.5a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a183a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle2); - particle2->setPosition(gauntlet->getPosition()); - particle2->setZOrder(1); - particle2->setScaleX(2.322f); - particle2->setScaleY(1.347f); - particle2->setID("gauntlet-particles-2"_spr); - CCParticleSystemQuad* particle3 = GameToolbox::particleFromString("19a-1a2a0.3a8a90a0a29a0a5a0a0a0a0a0a0a0a50a1a50a33a0.12549a0a0.0862745a0a0a0a1a0a30a1a50a30a0.12549a0a0.0862745a0a0a0a1a0a0.1a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a181a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle3); - particle3->setPosition(gauntlet->getPosition()); - particle3->setZOrder(1); - particle3->setScaleX(1.145f); - particle3->setScaleY(1.795f); - particle3->setID("gauntlet-particles-3"_spr); - CCParticleSystemQuad* particle4 = GameToolbox::particleFromString("31a-1a3a0.3a9a90a27a11a0a37a38a0a0a0a0a0a0a50a1a0a0a0.133333a0a0.0862745a0a0.0588235a0a1a0a50a1a0a0a0.0666667a0a0.0470588a0a0.0352941a0a1a0a0.5a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle4); - particle4->setPosition({1, -23}); - particle4->setZOrder(1); - particle4->setScaleX(1.270f); - particle4->setScaleY(1.832f); - particle4->setID("gauntlet-particles-4"_spr); - CCParticleSystemQuad* particle5 = GameToolbox::particleFromString("40a-1a3a0.3a12a90a13a11a0a48a78a0a0a0a0a0a0a5a3a0a0a0.439216a0a0.309804a0a0.129412a0a1a0a3a3a0a0a0.439216a0a0.309804a0a0.129412a0a1a0a0.1a0a0.3a0a0a0a0a0a0a0a0a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle5); - particle5->setPosition({2, -20}); - particle5->setZOrder(1); - particle5->setScaleX(1.205f); - particle5->setScaleY(1.280f); - particle5->setID("gauntlet-particles-5"_spr); - } else if (gauntletName == "Ice") { - CCSprite* sprite = CCSprite::create("ice_gauntlet.png"_spr); - sprite->setPosition(bg->getPosition()); - sprite->setScale(1.270f/scaleFactor); - sprite->setZOrder(0); - sprite->setID("gauntlet-background"_spr); - node->addChild(sprite); - - // Show gauntlets sprite again - gauntlet->setVisible(true); - gauntlet->setPosition({0, 15}); - gauntlet->setScale(1.050f); - gauntlet_node->setZOrder(2); - - // Particle - CCParticleSystemQuad* particle = GameToolbox::particleFromString("25a-1a1a0.3a19a90a90a0a0a0a0a0a0a0a0a0a0a12a1a0a98a0.0980392a0a0.176471a0a0.219608a0a1a0a36a1a0a0a0.0980392a0a0.176471a0a0.219608a0a1a0a0a0a0a0a0a0a0a0a0a0a0a2a1a0a0a0a191a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle); - particle->setPosition({0, 17}); - particle->setZOrder(1); - particle->setScaleX(3.760f); - particle->setScaleY(3.760f); - particle->setID("gauntlet-particles-1"_spr); - CCParticleSystemQuad* particle2 = GameToolbox::particleFromString("200a-1a4a0.3a46a90a0a18a0a24a0a0a0a0a0a0a0a1a1a0a39a0.980392a0a1a0a0.980392a0a1a0a1a1a98a41a0.980392a0a1a0a0.980392a0a1a0a0.3a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a14a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle2); - particle2->setPosition({11, 112}); - particle2->setZOrder(1); - particle2->setScaleX(2.322f); - particle2->setScaleY(3.097f); - particle2->setRotation(-174); - particle2->setID("gauntlet-particles-2"_spr); - CCParticleSystemQuad* particle3 = GameToolbox::particleFromString("44a-1a3a0.3a13a90a27a11a0a40a62a0a0a0a0a0a0a50a1a0a0a0.0431373a0a0.0627451a0a0.0666667a0a1a0a50a1a0a0a0.0431373a0a0.0627451a0a0.0666667a0a1a0a0.5a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle3); - particle3->setPosition({0, -11}); - particle3->setZOrder(1); - particle3->setScaleX(1.145f); - particle3->setScaleY(1.795f); - particle3->setID("gauntlet-particles-3"_spr); - CCParticleSystemQuad* particle4 = GameToolbox::particleFromString("38a-1a1.5a0.3a21a90a90a0a0a31a69a0a0a0a0a0a0a2a1a65a57a1a0a1a0a1a0a1a0a5a1a0a0a1a0a1a0a1a0a1a0a0.2a0a0.4a0a0a0a0a0a0a0a0a2a1a0a0a0a169a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle4); - particle4->setPosition(gauntlet->getPosition()); - particle4->setZOrder(1); - particle4->setScaleX(1.470f); - particle4->setScaleY(1.957f); - particle4->setID("gauntlet-particles-4"_spr); - } else if (gauntletName == "Poison") { - CCSprite* sprite = CCSprite::create("poison_gauntlet.png"_spr); - sprite->setPosition(bg->getPosition()); - sprite->setScale(1.270f/scaleFactor); - sprite->setZOrder(0); - sprite->setID("gauntlet-background"_spr); - node->addChild(sprite); - - // Particle - CCParticleSystemQuad* particle = GameToolbox::particleFromString("31a-1a3a0.3a9a90a27a11a0a37a64a0a0a0a0a0a0a50a1a0a0a0.054902a0a0.129412a0a0.0627451a0a1a0a50a1a0a0a0.054902a0a0.129412a0a0.0627451a0a1a0a0.5a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle); - particle->setPosition({0, 11}); - particle->setZOrder(1); - particle->setScaleX(1.260f); - particle->setScaleY(1.485f); - particle->setID("gauntlet-particles-1"_spr); - CCParticleSystemQuad* particle2 = GameToolbox::particleFromString("20a-1a3a0.3a6a90a22a11a0a33a55a0a0a0a0a0a0a1a1a0a0a0.137255a0a0.301961a0a0.133333a0a1a0a10a1a0a0a0.137255a0a0.301961a0a0.133333a0a1a0a0.1a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a40a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle2); - particle2->setPosition({-3, -13}); - particle2->setZOrder(1); - particle2->setScaleX(1.722f); - particle2->setScaleY(1.397f); - particle2->setID("gauntlet-particles-2"_spr); - CCParticleSystemQuad* particle3 = GameToolbox::particleFromString("4a-1a2.5a0.3a1a-90a0a0a10a35a0a0a-70a0a0a0a0a6a2a140a0a0.345098a0a0.690196a0a0.301961a0a1a0a4a2a140a0a0.345098a0a0.690196a0a0.301961a0a1a0a0a0a0a0a0a0a0a0a0a0a0a2a1a0a0a0a77a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle3); - particle3->setPosition({0, 109}); - particle3->setZOrder(1); - particle3->setScaleX(1.145f); - particle3->setScaleY(1.795f); - particle3->setID("gauntlet-particles-3"_spr); - CCParticleSystemQuad* particle4 = GameToolbox::particleFromString("40a-1a3a0.3a12a90a13a11a0a48a78a0a0a0a0a0a0a3a1a0a0a0.129412a0a0.439216a0a0.168627a0a1a0a1a1a0a0a0.129412a0a0.439216a0a0.168627a0a1a0a0.1a0a0.3a0a0a0a0a0a0a0a0a2a1a0a0a0a192a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle4); - particle4->setPosition(gauntlet->getPosition()); - particle4->setZOrder(1); - particle4->setScaleX(1.270f); - particle4->setScaleY(1.632f); - particle4->setID("gauntlet-particles-4"_spr); - } else if (gauntletName == "Shadow") { - CCSprite* sprite = CCSprite::create("shadow_gauntlet.png"_spr); - sprite->setPosition(bg->getPosition()); - sprite->setScale(1.745f/scaleFactor); - sprite->setZOrder(0); - sprite->setID("gauntlet-background"_spr); - node->addChild(sprite); - - // Show gauntlets sprite again - gauntlet->setVisible(true); - gauntlet->setPosition({2, 26}); - gauntlet->setScale(0.975f); - gauntlet_node->setZOrder(2); - - // Particle - CCParticleSystemQuad* particle = GameToolbox::particleFromString("48a-1a3a0.3a14a90a27a6a0a40a57a0a0a0a0a0a0a50a1a0a0a0.0509804a0a0.0235294a0a0.0666667a0a1a0a50a1a0a0a0.0509804a0a0.0235294a0a0.0666667a0a1a0a0.5a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle); - particle->setPosition({0, -15}); - particle->setZOrder(1); - particle->setScaleX(1.310f); - particle->setScaleY(1.735f); - particle->setID("gauntlet-particles-1"_spr); - CCParticleSystemQuad* particle2 = GameToolbox::particleFromString("62a-1a1a0.3a47a90a90a29a0a11a0a0a0a0a0a0a0a50a1a0a59a0.0784314a0a0.0235294a0a0.0941176a0a1a0a38a1a103a65a0.0784314a0a0.0235294a0a0.0941176a0a1a0a0.5a0a0.5a0a0a0a0a0a0a0a1a2a1a0a0a0a190a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle2); - particle2->setPosition({0, 25}); - particle2->setZOrder(1); - particle2->setScaleX(2.472f); - particle2->setScaleY(2.472f); - particle2->setID("gauntlet-particles-2"_spr); - CCParticleSystemQuad* particle3 = GameToolbox::particleFromString("62a-1a1a0.3a47a90a180a29a0a11a0a0a0a0a0a0a0a50a1a0a0a0.0313726a0a0.00392157a0a0.0470588a0a1a0a38a1a0a0a0.0313726a0a0.00392157a0a0.0470588a0a1a0a0.5a0a0.5a0a38a0a9a0a-85a26a1a2a1a0a0a0a183a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle3); - particle3->setPosition({0, 25}); - particle3->setZOrder(1); - particle3->setScaleX(1.470f); - particle3->setScaleY(1.470f); - particle3->setID("gauntlet-particles-3"_spr); - CCParticleSystemQuad* particle4 = GameToolbox::particleFromString("62a-1a1a0.3a47a90a180a29a0a11a0a0a0a0a0a0a0a50a1a0a0a0.0313726a0a0.00392157a0a0.0431373a0a1a0a38a1a0a0a0.0313726a0a0.00392157a0a0.0431373a0a1a0a0.5a0a0.5a0a38a0a9a0a-85a26a1a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle4); - particle4->setPosition({0, 25}); - particle4->setZOrder(1); - particle4->setScaleX(1.195f); - particle4->setScaleY(1.195f); - particle4->setID("gauntlet-particles-4"_spr); - CCParticleSystemQuad* particle5 = GameToolbox::particleFromString("40a-1a3a0.3a12a90a13a11a0a48a78a0a0a0a0a0a0a5a3a0a0a0.129412a0a0.439216a0a0.133333a0a1a0a3a3a0a0a0.129412a0a0.439216a0a0.133333a0a1a0a0.1a0a0.3a0a0a0a0a0a0a0a0a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle5); - particle5->setPosition({0, -16}); - particle5->setZOrder(1); - particle5->setScaleX(1.255f); - particle5->setScaleY(1.430f); - particle5->setID("gauntlet-particles-5"_spr); - } else if (gauntletName == "Lava") { - CCSprite* sprite = CCSprite::create("lava_gauntlet.png"_spr); - sprite->setPosition(bg->getPosition()); - sprite->setScale(1.270f/scaleFactor); - sprite->setZOrder(0); - sprite->setID("gauntlet-background"_spr); - node->addChild(sprite); - - // Show gauntlets sprite again - gauntlet->setVisible(true); - gauntlet->setPosition({0, 16}); - gauntlet->setScale(0.950f); - gauntlet_node->setZOrder(2); - - // Particle - CCParticleSystemQuad* particle = GameToolbox::particleFromString("40a-1a3a0.3a12a90a13a11a0a48a78a0a0a0a0a0a0a5a3a0a0a0.439216a0a0.309804a0a0.129412a0a1a0a3a3a0a0a0.439216a0a0.309804a0a0.129412a0a1a0a0.1a0a0.3a0a0a0a0a0a0a0a0a2a1a0a0a0a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle); - particle->setPosition({0, -16}); - particle->setZOrder(1); - particle->setScaleX(1.360f); - particle->setScaleY(1.360f); - particle->setID("gauntlet-particles-1"_spr); - CCParticleSystemQuad* particle2 = GameToolbox::particleFromString("59a-1a1a0.3a45a90a14a100a54a2a6a0a-135a0a46a0a0a20a8a0a0a1a0a0.756863a0a0.258824a0a1a0a1a1a0a0a1a0a0.694118a0a0.321569a0a1a0a0a0a0.1a0a0a0a0a0a0a0a0a2a1a0a0a1a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle2); - particle2->setPosition(gauntlet->getPosition()); - particle2->setZOrder(1); - particle2->setScaleX(1.522f); - particle2->setScaleY(1.547f); - particle2->setID("gauntlet-particles-2"_spr); - CCParticleSystemQuad* particle3 = GameToolbox::particleFromString("56a-1a1a0.3a43a90a16a103a81a2a6a0a-180a0a46a0a0a13a8a0a0a1a0a0.756863a0a0.258824a0a1a0a1a1a0a0a1a0a0.694118a0a0.321569a0a1a0a0a0a0.1a0a0a0a0a0a0a0a0a2a1a0a0a1a28a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle3); - particle3->setPosition(gauntlet->getPosition()); - particle3->setZOrder(1); - particle3->setScaleX(1.470f); - particle3->setScaleY(1.370f); - particle3->setID("gauntlet-particles-3"_spr); - CCParticleSystemQuad* particle4 = GameToolbox::particleFromString("56a-1a1a0.5a19a90a28a49a43a2a6a0a23a0a43a0a0a50a33a0a0a0.172549a0a0.160784a0a0.14902a0a0.47a0a50a38a0a0a0.247059a0a0.243137a0a0.239216a0a0.47a0a0a0a0.13a0a0a0a0a0a0a0a0a2a0a0a0a1a29a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle4); - particle4->setPosition(gauntlet->getPosition()); - particle4->setZOrder(1); - particle4->setScaleX(1.095f); - particle4->setScaleY(0.845f); - particle4->setID("gauntlet-particles-4"_spr); - CCParticleSystemQuad* particle5 = GameToolbox::particleFromString("59a-1a1a0.3a45a90a18a113a30a2a6a0a-192a0a46a0a0a20a8a0a0a1a0a0.505882a0a0.258824a0a1a0a1a1a0a0a1a0a0.505882a0a0.258824a0a1a0a0a0a0.1a0a0a0a0a0a0a0a0a2a1a0a0a1a158a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle5); - particle5->setPosition(gauntlet->getPosition()); - particle5->setZOrder(1); - particle5->setScaleX(1.255f); - particle5->setScaleY(1.430f); - particle5->setID("gauntlet-particles-5"_spr); - CCParticleSystemQuad* particle6 = GameToolbox::particleFromString("48a-1a3a0.3a14a90a27a6a0a40a57a0a0a0a0a0a0a50a1a0a0a0.0666667a0a0.0352941a0a0.0196078a0a1a0a50a1a0a0a0.0666667a0a0.0352941a0a0.0196078a0a1a0a0.5a0a1a0a0a0a0a0a0a0a0a2a1a0a0a0a46a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle6); - particle6->setPosition({0, -18}); - particle6->setZOrder(1); - particle6->setScaleX(1.235f); - particle6->setScaleY(1.835f); - particle6->setID("gauntlet-particles-6"_spr); - } else if (gauntletName == "Time") { - CCSprite* sprite = CCSprite::create("time_gauntlet.png"_spr); - sprite->setPosition(bg->getPosition()); - sprite->setScale(1.220f/scaleFactor); - sprite->setZOrder(0); - sprite->setID("gauntlet-background"_spr); - node->addChild(sprite); - } else if (gauntletName == "Spike") { - CCSprite* sprite = CCSprite::create("spike_gauntlet.png"_spr); - sprite->setPosition(bg->getPosition()); - sprite->setScale(1.920f/scaleFactor); - sprite->setZOrder(0); - sprite->setID("gauntlet-background"_spr); - node->addChild(sprite); - } else if (gauntletName == "Cyborg") { - CCSprite* sprite = CCSprite::create("cyborg_gauntlet.png"_spr); - sprite->setPosition(bg->getPosition()); - sprite->setScale(1.270f/scaleFactor); - sprite->setZOrder(0); - sprite->setID("gauntlet-background"_spr); - node->addChild(sprite); - } else if (gauntletName == "Inferno") { - CCSprite* sprite = CCSprite::create("inferno_gauntlet.png"_spr); - sprite->setPosition(bg->getPosition()); - sprite->setScale(1.270f/scaleFactor); - sprite->setZOrder(0); - sprite->setID("gauntlet-background"_spr); - node->addChild(sprite); - } else if (gauntletName == "Death") { - CCSprite* sprite = CCSprite::create("death_gauntlet.png"_spr); - sprite->setPosition(bg->getPosition()); - sprite->setScale(1.270f/scaleFactor); - sprite->setZOrder(0); - sprite->setID("gauntlet-background"_spr); - node->addChild(sprite); - } else if (gauntletName == "Magic") { - CCSprite* sprite = CCSprite::create("magic_gauntlet.png"_spr); - sprite->setPosition(bg->getPosition()); - sprite->setScale(1.270f/scaleFactor); - sprite->setZOrder(0); - sprite->setID("gauntlet-background"_spr); - node->addChild(sprite); - } else if (gauntletName == "Chaos") { - CCSprite* sprite = CCSprite::create("chaos_gauntlet.png"_spr); - sprite->setPosition(bg->getPosition()); - sprite->setScale(1.270f/scaleFactor); - sprite->setZOrder(0); - sprite->setID("gauntlet-background"_spr); - node->addChild(sprite); - } else if (gauntletName == "Force") { - CCSprite* sprite = CCSprite::create("force_gauntlet.png"_spr); - sprite->setPosition(bg->getPosition()); - sprite->setScale(1.270f/scaleFactor); - sprite->setZOrder(0); - sprite->setID("gauntlet-background"_spr); - node->addChild(sprite); - } else if (gauntletName == "NCS I") { - CCSprite* sprite = CCSprite::create("ncs_gauntlet_1.png"_spr); - sprite->setPosition(bg->getPosition()); - sprite->setScale(1.270f/scaleFactor); - sprite->setZOrder(0); - sprite->setID("gauntlet-background"_spr); - node->addChild(sprite); + if (!json.contains(gauntletName)) { + restoreVisibility(); + continue; + } - // Show gauntlets sprite again - gauntlet->setVisible(true); - gauntlet->setPosition({0, 16}); - gauntlet->setScale(0.9f); - gauntlet_node->setZOrder(3); + auto gdOpt = matjson::Serialize::fromJson(json[gauntletName]); + if (gdOpt.isErr()) { + log::warn("Couldn't parse gauntlet '{}': {}", gauntletName, gdOpt.unwrapErr()); + restoreVisibility(); + continue; + } + auto const& gd = gdOpt.unwrap(); - CCParticleSystemQuad* particle = GameToolbox::particleFromString("25a-1a1a0.3a19a90a90a0a0a0a0a0a0a0a0a0a0a23a1a0a98a0.0980392a0a0.176471a0a0.219608a0a1a0a50a1a0a0a0.0980392a0a0.176471a0a0.219608a0a1a0a0a0a0a0a0a0a0a0a0a0a0a2a1a0a0a0a185a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle); - particle->setPosition({0, 20}); - particle->setZOrder(2); - particle->setScale(2.060f); - particle->setID("gauntlet-particles-1"_spr); + auto bgPos = bg->getPosition(); + bgPos.y += gd.bgOffsetY; - CCParticleSystemQuad* particle2 = GameToolbox::particleFromString("25a-1a1a0.3a19a90a90a0a0a0a0a0a0a0a0a0a0a12a1a0a98a0.0980392a0a0.176471a0a0.219608a0a1a0a36a1a0a0a0.0980392a0a0.176471a0a0.219608a0a1a0a0a0a0a0a0a0a0a0a0a0a0a2a1a0a0a0a167a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle2); - particle2->setPosition(gauntlet->getPosition()); - particle2->setZOrder(2); - particle2->setScale(4.210f); - particle2->setID("gauntlet-particles-2"_spr); + auto* bgSprite = CCSprite::create(fmt::format("{}"_spr, gd.background).c_str()); + bgSprite->setPosition(bgPos); + bgSprite->setScale(gd.scaleBg / scaleFactor); + bgSprite->setZOrder(0); + bgSprite->setID("gauntlet-background"_spr); + node->addChild(bgSprite); - CCParticleSystemQuad* particle3 = GameToolbox::particleFromString("30a-1a2.5a0.3a10a90a12a19a0a23a0a0a11a0a0a0a0a4a1a0a0a0.313726a0a0.458824a0a0.529412a0a1a0a2a1a0a0a0.313726a0a0.458824a0a0.529412a0a1a0a0a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a19a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle3); - particle3->setZOrder(0); - particle3->setPosition({0, -81}); - particle3->setScale(2.035f); - particle3->setID("gauntlet-particles-3"_spr); - } else if (gauntletName == "NCS II") { - CCSprite* sprite = CCSprite::create("ncs_gauntlet_2.png"_spr); - sprite->setPosition(bg->getPosition()); - sprite->setScale(1.270f/scaleFactor); - sprite->setZOrder(0); - sprite->setID("gauntlet-background"_spr); - node->addChild(sprite); + if (gd.itemZOrder != 0) item->setZOrder(gd.itemZOrder); + if (gd.reverseGauntlet) reverseGauntlet(node); - // Show gauntlets sprite again + if (gd.scaleGauntlet != 0.f) { gauntlet->setVisible(true); - gauntlet->setPosition({0, 16}); - gauntlet->setScale(0.9f); - gauntlet_node->setZOrder(3); - - CCParticleSystemQuad* particle = GameToolbox::particleFromString("25a-1a1a0.3a19a90a90a0a0a0a0a0a0a0a0a0a0a23a1a0a98a0.301961a0a0.156863a0a0.341176a0a1a0a50a1a0a0a0.301961a0a0.156863a0a0.341176a0a1a0a0a0a0a0a0a0a0a0a0a0a0a2a1a0a0a0a185a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle); - particle->setPosition({0, 20}); - particle->setZOrder(2); - particle->setScale(2.060f); - particle->setID("gauntlet-particles-1"_spr); - - CCParticleSystemQuad* particle2 = GameToolbox::particleFromString("25a-1a1a0.3a19a90a90a0a0a0a0a0a0a0a0a0a0a12a1a0a98a0.301961a0a0.156863a0a0.341176a0a1a0a36a1a0a0a0.301961a0a0.156863a0a0.341176a0a1a0a0a0a0a0a0a0a0a0a0a0a0a2a1a0a0a0a167a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle2); - particle2->setPosition(gauntlet->getPosition()); - particle2->setZOrder(2); - particle2->setScale(4.210f); - particle2->setID("gauntlet-particles-2"_spr); + gauntlet->setPosition(gd.gauntletPos); + gauntlet->setScale(gd.scaleGauntlet); + if (gd.gauntletRotation != 0.f) gauntlet->setRotation(gd.gauntletRotation); + if (gd.gauntletFlipX) gauntlet->setFlipX(true); + gauntlet_node->setZOrder(gd.gauntletZOrder); + } - CCParticleSystemQuad* particle3 = GameToolbox::particleFromString("30a-1a2.5a0.3a10a90a12a19a0a23a0a0a11a0a0a0a0a4a1a0a0a0.482353a0a0.317647a0a0.533333a0a1a0a2a1a0a0a0.482353a0a0.317647a0a0.533333a0a1a0a0a0a0.5a0a0a0a0a0a0a0a0a2a1a0a0a0a19a0a0a0a0a0a0a0a0a0a0a0a0a0a0", NULL, false); - node->addChild(particle3); - particle3->setZOrder(0); - particle3->setPosition({0, -81}); - particle3->setScale(2.035f); - particle3->setID("gauntlet-particles-3"_spr); - } else if (gauntletName == "Discord") { - CCSprite* sprite = CCSprite::create("discord_gauntlet.png"_spr); - sprite->setPosition({bg->getPositionX(), bg->getPositionY() + 3}); - sprite->setScale(1.245f/scaleFactor); - sprite->setZOrder(0); - item->setZOrder(1); - sprite->setID("gauntlet-background"_spr); - node->addChild(sprite); - } else if (gauntletName == "Space") { - CCSprite* sprite = CCSprite::create("space_gauntlet.png"_spr); - sprite->setPosition({bg->getPositionX(), bg->getPositionY() + 3}); - sprite->setScale(1.245f/scaleFactor); - sprite->setZOrder(0); - item->setZOrder(1); - sprite->setID("gauntlet-background"_spr); - node->addChild(sprite); - } else if (gauntletName == "Cosmos") { - CCSprite* sprite = CCSprite::create("cosmos_gauntlet.png"_spr); - sprite->setPosition({bg->getPositionX(), bg->getPositionY() + 3}); - sprite->setScale(1.245f/scaleFactor); - sprite->setZOrder(0); - item->setZOrder(1); - sprite->setID("gauntlet-background"_spr); - node->addChild(sprite); - } else { - bg->setVisible(true); - label->setVisible(true); - label_shadow->setVisible(true); - label_gauntlet->setVisible(true); - label_shadow_gauntlet->setVisible(true); - gauntlet->setVisible(true); - gauntlet_shadow->setVisible(true); + for (size_t k = 0; k < gd.particles.size(); k++) { + auto const& pd = gd.particles[k]; + auto* p = GameToolbox::particleFromString(pd.particle, nullptr, false); + node->addChild(p); + p->setZOrder(pd.zOrder); + p->setPosition(pd.useGauntletPos ? gauntlet->getPosition() : pd.pos); + p->setScaleX(pd.scale.x); + p->setScaleY(pd.scale.y); + if (pd.rotation != 0.f) p->setRotation(pd.rotation); + p->setID(fmt::format("gauntlet-particles-{}"_spr, k + 1)); } } } From 381604cf4987883d342fa60e2a8dd7fee018f1b5 Mon Sep 17 00:00:00 2001 From: Jouca Date: Fri, 24 Apr 2026 01:44:50 +0200 Subject: [PATCH 21/32] release(rewrite): Push beta version --- changelog.md | 6 ++++++ mod.json | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index dfead65..ef3ccd2 100644 --- a/changelog.md +++ b/changelog.md @@ -1,4 +1,10 @@ # Changelog +## v1.6.0-beta.3 (2026-04-24) +* Added back leaderboards & moderator list. +* Added back custom backgrounds. +* Fixed daily chests being ignore on claiming. +* Fixed GDUtils badges crashing when clicking to view details. + ## v1.6.0-beta.2 (2026-03-08) * Fixed chests crashing upon opening for Android. diff --git a/mod.json b/mod.json index 6dcd603..12faf62 100644 --- a/mod.json +++ b/mod.json @@ -1,6 +1,6 @@ { "geode": "5.6.1", - "version": "v1.6.0-beta.2", + "version": "v1.6.0-beta.3", "gd": { "win": "2.2081", "mac": "2.2081", From 09f54788cc2f6d80249dc47024f95111a8c8506d Mon Sep 17 00:00:00 2001 From: Jouca Date: Fri, 24 Apr 2026 02:45:54 +0200 Subject: [PATCH 22/32] fix(rewrite): Fixed comments colors for roles --- src/hooks/CustomBadges.cpp | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/src/hooks/CustomBadges.cpp b/src/hooks/CustomBadges.cpp index a260818..daeff83 100644 --- a/src/hooks/CustomBadges.cpp +++ b/src/hooks/CustomBadges.cpp @@ -397,6 +397,18 @@ class $modify(CustomBadgesCommentCell, CommentCell) { } if (accountID_data == accountID) { + bool isCustomColor = (color.r != 255 || color.g != 255 || color.b != 255); + if (isCustomColor && !colorSet) { + if (auto textArea = m_mainLayer->getChildByType(0)) { + CCArrayExt children = textArea->m_label->getChildren(); + for (auto label : children) label->setColor(color); + colorSet = true; + } else if (auto commentText = static_cast(m_mainLayer->getChildByID("comment-text-label"))) { + commentText->setColor(color); + cell->m_comment->m_color = color; + colorSet = true; + } + } if (!m_mainLayer->getChildByIDRecursive(badge_id)) { auto* sprite = Build::createSpriteName(fmt::format("{}"_spr, badge_sprite).c_str()) .scale(badge_scale) @@ -409,22 +421,6 @@ class $modify(CustomBadgesCommentCell, CommentCell) { FLAlertLayer::create(nullptr, title.c_str(), desc.c_str(), "OK", nullptr, width)->show(); } }); - if (auto textArea = cell->getChildByType(0)) { - CCArrayExt children = textArea->m_label->getChildren(); - for (auto label : children) { - if (color.r != 255 && color.g != 255 && color.b != 255 && !colorSet) { - label->setColor(color); - colorSet = true; - } - } - } - if (auto commentText = static_cast(cell->getChildByID("comment-text-label"))) { - if (color.r != 255 && color.g != 255 && color.b != 255 && !colorSet) { - commentText->setColor(color); - cell->m_comment->m_color = color; - colorSet = true; - } - } if (cell->getChildByIDRecursive("percentage-label")) { menu->insertBefore(btn, cell->getChildByIDRecursive("percentage-label")); } else { From f63b777ddd824b6be437b6303148062314add277 Mon Sep 17 00:00:00 2001 From: Jouca Date: Tue, 12 May 2026 17:33:35 +0200 Subject: [PATCH 23/32] fix(main): Fix daily chest delegate --- src/modules/events/DailyChest.cpp | 14 ++++++++++++++ src/modules/events/DailyChest.hpp | 1 + 2 files changed, 15 insertions(+) diff --git a/src/modules/events/DailyChest.cpp b/src/modules/events/DailyChest.cpp index 2ae72a8..0a41bdc 100644 --- a/src/modules/events/DailyChest.cpp +++ b/src/modules/events/DailyChest.cpp @@ -46,6 +46,17 @@ struct ChestMsg { static std::optional> g_chestTx; static DailyChest* g_pendingRefresh = nullptr; +static void clearDelegateIfSelf(DailyChest* self) { + auto glm = GameLevelManager::sharedState(); + if (glm && glm->m_GJRewardDelegate == self) { + glm->m_GJRewardDelegate = nullptr; + } +} + +DailyChest::~DailyChest() { + clearDelegateIfSelf(this); +} + void DailyChest::getRewards() { auto glm = GameLevelManager::sharedState(); if (glm) { @@ -59,6 +70,7 @@ void DailyChest::rewardsStatusFinished(int p0) { auto gsm = GameStatsManager::sharedState(); if (gsm->m_rewardItems->count() == 0) { log::debug("[DailyChest] no reward items, aborting"); + clearDelegateIfSelf(this); if (g_pendingRefresh == this) { g_pendingRefresh = nullptr; this->release(); } return; } @@ -77,11 +89,13 @@ void DailyChest::rewardsStatusFinished(int p0) { .largeTime = t2 }); } + clearDelegateIfSelf(this); if (g_pendingRefresh == this) { g_pendingRefresh = nullptr; this->release(); } }; void DailyChest::rewardsStatusFailed() { log::error("[DailyChest] Failed to get rewards"); + clearDelegateIfSelf(this); if (g_pendingRefresh == this) { g_pendingRefresh = nullptr; this->release(); } }; diff --git a/src/modules/events/DailyChest.hpp b/src/modules/events/DailyChest.hpp index 3e2cf05..a89bf50 100644 --- a/src/modules/events/DailyChest.hpp +++ b/src/modules/events/DailyChest.hpp @@ -5,6 +5,7 @@ using namespace geode::prelude; class DailyChest : public CCObject, public GJRewardDelegate { public: + ~DailyChest(); void getRewards(); void rewardsStatusFinished(int p0) override; void rewardsStatusFailed() override; From 81fd701a05dde6799901317c9f578717db03215c Mon Sep 17 00:00:00 2001 From: Jouca Date: Tue, 12 May 2026 17:43:35 +0200 Subject: [PATCH 24/32] update(rewrite): Bump geode version to 5.7.1 --- mod.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mod.json b/mod.json index 12faf62..2a82ce6 100644 --- a/mod.json +++ b/mod.json @@ -1,5 +1,5 @@ { - "geode": "5.6.1", + "geode": "5.7.1", "version": "v1.6.0-beta.3", "gd": { "win": "2.2081", From 969347a793c52771f4e1f132e20591d3de72b0ef Mon Sep 17 00:00:00 2001 From: Jouca Date: Wed, 13 May 2026 00:28:48 +0200 Subject: [PATCH 25/32] release(rewrite): Bump version to v1.6.0-beta.4 --- mod.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mod.json b/mod.json index 2a82ce6..7378e0e 100644 --- a/mod.json +++ b/mod.json @@ -1,6 +1,6 @@ { "geode": "5.7.1", - "version": "v1.6.0-beta.3", + "version": "v1.6.0-beta.4", "gd": { "win": "2.2081", "mac": "2.2081", From f692673722e28488f898f543d7c08cf39d0c3790 Mon Sep 17 00:00:00 2001 From: Jouca Date: Wed, 13 May 2026 00:32:29 +0200 Subject: [PATCH 26/32] update(rewrite): Revert geode version to 5.6.1 --- changelog.md | 4 ++++ mod.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index ef3ccd2..6883c80 100644 --- a/changelog.md +++ b/changelog.md @@ -1,4 +1,8 @@ # Changelog +## v1.6.0-beta.4 (2026-05-13) +* Added 2025 GD Awards badge. +* Fixed daily chests crashing. + ## v1.6.0-beta.3 (2026-04-24) * Added back leaderboards & moderator list. * Added back custom backgrounds. diff --git a/mod.json b/mod.json index 7378e0e..5649819 100644 --- a/mod.json +++ b/mod.json @@ -1,5 +1,5 @@ { - "geode": "5.7.1", + "geode": "5.6.1", "version": "v1.6.0-beta.4", "gd": { "win": "2.2081", From 4f6d9e5b4c7d9e4da364f1785fdfef5cefdd8cf3 Mon Sep 17 00:00:00 2001 From: Jouca Date: Wed, 13 May 2026 11:41:29 +0200 Subject: [PATCH 27/32] update(rewrite): Remove discord-rpc submodule and related binaries --- .gitmodules | 5 +---- libs/discord-rpc | 1 - libs/link-libs/discord-rpc/discord-rpc.lib | Bin 183534 -> 0 bytes libs/link-libs/discord-rpc/libdiscord-rpc.a | Bin 672376 -> 0 bytes 4 files changed, 1 insertion(+), 5 deletions(-) delete mode 160000 libs/discord-rpc delete mode 100644 libs/link-libs/discord-rpc/discord-rpc.lib delete mode 100644 libs/link-libs/discord-rpc/libdiscord-rpc.a diff --git a/.gitmodules b/.gitmodules index 881f23f..893dce2 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,3 @@ -[submodule "libs/pl_mpeg"] +[submodule "libs/pl_mpeg"] path = libs/pl_mpeg url = https://github.com/phoboslab/pl_mpeg -[submodule "libs/discord-rpc"] - path = libs/discord-rpc - url = https://github.com/discord/discord-rpc diff --git a/libs/discord-rpc b/libs/discord-rpc deleted file mode 160000 index 963aa9f..0000000 --- a/libs/discord-rpc +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 963aa9f3e5ce81a4682c6ca3d136cddda614db33 diff --git a/libs/link-libs/discord-rpc/discord-rpc.lib b/libs/link-libs/discord-rpc/discord-rpc.lib deleted file mode 100644 index 99ac434b4d74fd9650957752379853536988be1b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 183534 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

QERONf_F1;CU@`02am{F8$~VR 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 z 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 From b235bb9689c00d0d25cd9221c7ccc284118ef793 Mon Sep 17 00:00:00 2001 From: FireMario211 <17692105+FireMario211@users.noreply.github.com> Date: Tue, 30 Jun 2026 13:17:41 -0400 Subject: [PATCH 28/32] fix aredl (pointercrate blocked unfortunately) --- changelog.md | 4 ++++ mod.json | 4 ++-- src/hooks/LevelInfoLayer.cpp | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/changelog.md b/changelog.md index 6883c80..2f4ec72 100644 --- a/changelog.md +++ b/changelog.md @@ -1,4 +1,8 @@ # Changelog +## v1.6.0-beta.5 (2026-06-30) +* Adjusted default parameters for the demon list to be AreDL +* Fixed AreDL demon placement not working. + ## v1.6.0-beta.4 (2026-05-13) * Added 2025 GD Awards badge. * Fixed daily chests crashing. diff --git a/mod.json b/mod.json index 5649819..49ad7cc 100644 --- a/mod.json +++ b/mod.json @@ -1,6 +1,6 @@ { "geode": "5.6.1", - "version": "v1.6.0-beta.4", + "version": "v1.6.0-beta.5", "gd": { "win": "2.2081", "mac": "2.2081", @@ -297,7 +297,7 @@ "name": "Demon List Selection", "description": "Whether or not to select between Pointercrate or AreDL for placements.", "type": "string", - "default": "Pointercrate", + "default": "AreDL", "one-of": ["Pointercrate", "AreDL"] }, "socketServer": { diff --git a/src/hooks/LevelInfoLayer.cpp b/src/hooks/LevelInfoLayer.cpp index 0806a60..edf0cc7 100644 --- a/src/hooks/LevelInfoLayer.cpp +++ b/src/hooks/LevelInfoLayer.cpp @@ -85,7 +85,7 @@ class $modify(CustomLevelInfoLayer, LevelInfoLayer) { if (listId == "Pointercrate") { url = fmt::format("https://pointercrate.com/api/v2/demons/listed/?level_id={}", levelID); } else { - url = fmt::format("https://api.aredl.net/api/aredl/levels/{}", levelID); + url = fmt::format("https://api.aredl.net/v2/api/aredl/levels/{}", levelID); } geode::utils::web::WebRequest request = web::WebRequest(); m_fields->m_listener.spawn("@loaderDemonListLevelInfo", request.get(url.c_str()), [this, loading_circle, positionLabel, demonSpr, levelID](web::WebResponse value) { From 6feb03a85166957c6142151bc8233f6dc3fcb3eb Mon Sep 17 00:00:00 2001 From: Jouca Date: Mon, 3 Aug 2026 14:18:49 +0200 Subject: [PATCH 29/32] update(rewrite): enhance AMQT::setupChannel with queue expiration safety --- mod.json | 8 ++++---- src/network/AMQClient.cpp | 15 +++++++++++++-- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/mod.json b/mod.json index 49ad7cc..a0164e5 100644 --- a/mod.json +++ b/mod.json @@ -1,6 +1,6 @@ { - "geode": "5.6.1", - "version": "v1.6.0-beta.5", + "geode": "5.8.2", + "version": "v1.6.0-beta.6", "gd": { "win": "2.2081", "mac": "2.2081", @@ -16,8 +16,8 @@ "source": "https://github.com/Jouca/GDUtils-Geode" }, "dependencies": { - "geode.node-ids": ">=v1.21.1", - "jouca.badgesapi": ">=v1.4.1" + "geode.node-ids": ">=v1.23.3", + "jouca.badgesapi": ">=v1.5.0" }, "api": { "include": [ "include/*.hpp" ] diff --git a/src/network/AMQClient.cpp b/src/network/AMQClient.cpp index 0e23e13..06512ad 100644 --- a/src/network/AMQClient.cpp +++ b/src/network/AMQClient.cpp @@ -109,11 +109,22 @@ void AMQT::setupChannel() { } else { amqp_queue_delete(m_connection, 1, amqp_cstring_bytes(queueName.c_str()), 0, 0); amqp_get_rpc_reply(m_connection); + + // server-side safety net: if the TCP connection ever fails to close cleanly + // (so exclusive doesn't trigger deletion), the queue expires anyway. + // 1 hour, well above the heartbeat (10s), so this should never trigger in normal usage. + amqp_table_entry_t q_arg_entries[1]; + q_arg_entries[0] = (amqp_table_entry_t) { + .key = amqp_cstring_bytes("x-expires"), + .value = {.kind = AMQP_FIELD_KIND_I32, .value = {.i32 = 3600000}} + }; + amqp_table_t q_arg_table = {.num_entries = 1, .entries = q_arg_entries}; + r = amqp_queue_declare( m_connection, 1, amqp_cstring_bytes(queueName.c_str()), - 0, 0, 0, 1, - amqp_empty_table + 0, 0, 1, 1, + q_arg_table ); } { From ce3241a593a1b882f56a048906ff2495ecda362a Mon Sep 17 00:00:00 2001 From: Jouca Date: Mon, 3 Aug 2026 14:20:50 +0200 Subject: [PATCH 30/32] update(mod.json): upgrade geode version to 5.8.2 --- test/mod.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/mod.json b/test/mod.json index f15f939..2bf5819 100644 --- a/test/mod.json +++ b/test/mod.json @@ -1,5 +1,5 @@ { - "geode": "5.6.1", + "geode": "5.8.2", "gd": { "win": "2.2081", "android": "2.2081", From cf811bc9d42a9a98fd281cf5ceb385ae0ac6da91 Mon Sep 17 00:00:00 2001 From: Jouca Date: Mon, 3 Aug 2026 14:30:24 +0200 Subject: [PATCH 31/32] update(badges): replace badge images with updated versions --- resources/badges/ul_dev_badge.png | Bin 8055 -> 20492 bytes resources/badges/ul_helper_badge.png | Bin 7880 -> 20166 bytes resources/badges/ul_officer_badge.png | Bin 7725 -> 20256 bytes resources/badges/ul_trainee_badge.png | Bin 6866 -> 20123 bytes 4 files changed, 0 insertions(+), 0 deletions(-) diff --git a/resources/badges/ul_dev_badge.png b/resources/badges/ul_dev_badge.png index 600a7e076dadd1e49b790a6e8777cb5d84ea8ca2..55d36671d532675ba544ce49748ce0c445d66db8 100644 GIT binary patch literal 20492 zcmZ@tC_K@Ij^a+Ip_dj zV`1ZDWZ`CHVOL{i<7MOIW#^)2Vc}(A=|W_k|33rl9L#=Nc>VtaCX^x^Km%z0_YST< z?aWyw)(@@){~&QMN65jH;kxsd4Uva42cxF zSTac$xfC{rSP7SMrf3LoP7Jab&hUh)fE-1KhLqHeYgN9%&i#<~DR+x_n!T}6nnWrn zPzbqJ*4@G**DKd&s_Q9vnn&`6vh4EmGVbGJe>6#126dPfpkt#T@SrAy(65{!cNkeC zT4tD1DJWialpQeZB|?=%nlw7Iq2KGKAItZ+Vz8`Q=+jkGTYL4OQ`Ylm?9tmWx#|^~ zF^~R)qh|m+D{_#GOjIy$7^i=fI;S&1<+MSL>JGe4Q@zGpGsd6idmpCI=M8R_^#uwf z7?}!I=9h)D&=vl3)##I7Rdcr6lD0aTbL1W`HH^p%bJ8gH-O+Rz3KmYs)Qa`6Q@}A_ z#)l%$!TxBkd77TH^k#ZWTA2zQ>dTYEe!%^zl3?ByM|(%d$>n9K7J~`{*0-R1CWL;6 z$PrgmRx&b|qp6gw&&NM(Rx>1{`@TFM6Z88Q9$SedOfo3g@>W6E;%3OGKdr88i=18E z-REX!m5F428}v(m>E8H#uO#{^;wZv|%GY85T@fJ|)Uu(D9LXV@qa>T#++^a~a80sp zJ4giwUcCj0I&)~RdN#%!biz-k9Hv^(hCxNxCdW~BONzO(nyBgO;t!f6_zG^x!e+-k zUllV4%{InM5aKuv?BI$Eem$M`CXFzG z5>jqnpBQOuB``<+Y160n zTYud9fjlL99E=kWoyD7R_!e(%V(f7tQpY|IXWIRIiAR z4Ts&@_gH~Pt;ZEpWGqg`zjY0cCvwoXQfd>X^0_n&$!Q`eV(ItfqPs=ECl8Nf!DIO@ z!_S{KK0LR7!5m*+YV1`lgr}2FT z&B4_N%|(7ZZ3)2>P?D9=m4rcT&XvgWoK-bGzioXf*x1mo+NQ?F#_ofqH2!_k7VZsQ z1M@pxu0_7HnC?4-S_ZR8R9C(Q>^Ja44x(&5u@0%|YYsmCbC@2)Hk@d6+EbDll=%(S z>rh0~{+Zixb<$$uL;zF&w-|y7^c=b&MQ<}yyu$+aTR$HEf!<_Yl!OZI$Uhhfk}<4Q zu;4%K>{4_cy40Z&@fKt=R4aRBM4i+a*X^z&Z4Mg%%p_>YwkSzpg97p zCr9rhAsjtQX&uAd8UbS5Ceh0=A{th=O$JQeAM6F4@kOk|1EM+Z}USIHNDwhWS0y27sBcDcV55l+*?i%OUe zQqyn!yApnRL!USQ4V=JCphA5ifjcO9vK*bB?$8QD!ezwm(Q))s&Y+uJyRxG#7Tu=P zBp}FOF%HsmndF$8nuWe?^au?_T?7nKRTJ=xve};e3%9r6>E9K9aPTQ+ncNsL!)zr}|*vAShL6IsIa*TDDfV{TzR_3Io8rVro77iQ<{QA~QXN8$`%ZU0Cj!K*E+R<}bQEApF&xz@Q zS*d%HyOTfQ!FFKx^_--bYH5%~Do=}j$pn12aDY%t)uto6YQi+X&HH399ky?pV->C-4`d23JlT|p#vv{HdRF^s|C5Oj zUL4g+RO}H8^+OyjkV8YH`r|q9(Pdg-`Of_Jkd0j-CBEtNWNkq3Tx7>pcL&z9yrkrC zLibgrE$6rkGygQJB;n*l20QCeeiE((MnpwTNHnrB>Hx)~H)-UGAwQ||`?Azx;j zF#WYB>Hh4KMg2-;LH}>6RDyb^J~}bcCb-c-ap;vmWjv$}UyPzaFlan6?xQkG#}AHW zqe9@?DV|9s4F(+MycJj!1dtpkQ@sD{jmYdt-{Z$#Jly$($;+(Z)@qpmEV%#YxQin} zI17Rkfn~|d`>AHn>kWe55BgmpIshfxkdnTBOmiyZpLORt;JHnVz^GGBJ=x#N+U~Jr zTGiIC&o}KRD^viFQ1BivMmv0j=`fhshy}^9!tR>sl71dBffqwkEKgWP_aXzhLG$%- z_Dd%<h+- z)y+=Ig)_E#Y%v{)sFm$;zmp_!e;;)=Y=8J%;SS>V>>=kSqbnOTEU=OdLkv|HkrN_e zh#k+407ZUVsv%|oI++Yzq$mcrI;0lqYWOmkqX**%m`uKcmJuZwo{_P64A73Qdffv!!R)7$7t|u8hHWP#4yobPF?!SwfD>?)iB^z7P)E;ky)XtMJTx^aL zH0^qx*q#-P`?Fr~KS$N;`5`voY$dT57KF3e#$ka`_#J^%Ltx5A9K2bB?z#l^W*w>Q zET=}<4yztf4=XKrrvT#C{dJetvn5)F^hTU4qNCVL)WH2_$v%nwcLE(7{^`BWyQ#fi z2;}0eK!1Qq^+)*9>-X*8pYx>;YvA4Y;4GlJl&rGv%+s=B5>>BrW*Yb-Py%)57YxyM zX_o!&tR%7c?=hEVYDbk7#-xM<^Dg{|#37@~n<2xQAP72We=0xpxOr>&d373~la3Udl^l~%;>Zs<5B!ubbCeaLpxoVA*t$n6+;OdGw z^V!#hi@1sx%hw_mE@>_;tw}YFU9Xd70JbH5%VgR@<%nsT8!ed*($wHM1BX4lhA z81?G~R`t&1*-{qYg{|LedLfDVaCVF|8dFDapjFEzLS3u@QW}!PokYVED_K6HR*F2F z&bUP$lMkPoez`&nAQ$=8NWw{xbY-MPz}`(a%q59nmPJ};kJ-dzeN>uNyE}wmQ*_Wz z8K|)AKdr}Ol{`0)ND`M)gv(`5kks8HG)&-v5EByv5a0d^wE0CXgbk)uXCzX)Y>bmQ zYDR+;wNo-NH&a``flosjy27X+|_PaGwhaA`xETb`=iu(4E;^w6ff?I zI~ME%IpQ+>!g|&-wyjgpdaef!D;9rpj-8EBuO0uXY+g>?*%#!P^GX_Uq5LFPM){}q z2OVq{2ZM~tOW&dbJn0fzSX&?c_sp^Cn|v=K)G!rq7iuU6UVQ}JsrE=%>M2cWt?5d+ z;CKO_S|bw+o|S$RFfZp*^|JL&0sI?jnz$quCO!t|0|D$Y5or4zZ@t-Ty&a=p_3ya2 z<~M|-1*YhV?Nc9R`?*V6T2g1oh0#QEUo_2+Sv0d+P=3bXu$o-J1+qdfyN$D6$uhM; zR&)GEnF6++dcI}jhV7K*9{tvCv~W7B{^LB!u{^W57~0(OlxnDQl0=!1&)hU#K6~bL z{N&%uN}VgV7|)9ZUJK99Yl(EFJ#I2xqWT88YjUH^9RUC`!Uw7w#X#+m6v}Yr!3t-r zVmiX=j=tX$RS(9{!VCZPg&{|hG7;dGD5~yjL~VBBiU}<_ub;JY*Pk}`h0eyweSZ6) z=kwv#c|RA0k;+K9XFf_BUQ>r(lsNxe?l+3Y^5ShU1k(1gX{>Ql3>Vej3wWoU$sK*k zA~|TA-bJ%vJj!-KTGDA+s$7p*ZU=D3{va5pDPb9M{CHS)8Kq1+x2P5nx9KVZngx?= zpH|i$rs~@7fS@FkgKA0Ed%26@Q3cQC^&a>AaS?9QtQN2DpighVZ0fu%qimINgjM?u zuzF~hY^;+W!Mu9G*maV{GPpVRgNK2Rm{^W)W~ zWRaxaxowYE2%uK4wV$GCuoM3S$@uYn72uhx`DkBF_xmqQLmPuB*G4wJ!y9}5GOl=| za8}SssQqVKRSr)=!MICA7s9VwluD;680hFo6jm$gdsJ5Fs{BE+dI?-+fqrx_B@GmM ztpsO0&4z2V!>Eg!S$sr^Hm&bE--nI|L|Kr&ElilWAy_3ppqrASG{q=UDH1m(U%WvR zv|ey?*8Zx%dF^mKjhouu6xteVIAFv`xrYtZHujL4G-6HlhroMzs12Aze#{FwqOAMA z%~Z00yR;T6FE4MQF4;ShK)x&oIGoNi%ta}|=)Ph%>J3bw;)LO=aY10=NBEKJbIJ4h zJS^lq&iLp4wG)`{W(`qRn!ALz?8d6x+w#x;FKIYoAJLWo;GZP`{uef?fwjGbg)X0+ zf2zXPuo-*E7}!C9*q&sN7#2E|l&w=tUjzeh?6nDL5l1W(W*I$x6xP^r;r*eo>&uH6 zqw7cH$+4=dtrO+Gh z)4B^X=#kO;Z9e%0k+}fz+x;;&@|l*6pBwo{(xJ>gENS2 z89}qgW@D-kt%>F5aVkfXUxHJX#MhTit9QLYyOm8-*4jkU&zaeT&e@n*Oxct!bOz}j z%rwPPSId5{L3@E$!Fdopa201ac(3u3i;2}WWi5&UZ0a2Q_37<&;9SA#>RLrC(8*#e zS0L;V42`J64)(kqjEuu> zA*Ue5sUo9qWztx=$6HZd9Rp(Wc}WVq5mQrBx%#4MOzZd#IwroHS>|y9kJ}C7Os3bo zXLT5!Y3a*H6DIESWT_q=ja5bHl_V;H^@ftgdIVcP2E$rjCYz>m74Oa*K)kgVhF#=e zh_OAd>QZ2gN>X4*UxaZU(qfU9O-lBW%&)Uzi*or+zNJ8C^k0r2`$tNYW7aizuTSA3 z!H&y>vOR9Y=x>V)PYwe^(EsWQxM&hNSb3(Q4p0VILGS?Kba~`*Yb9|qtj{~};?xL4 z0rb2MQW$K0QX=#>+}Thn(83VFFhb+wGj-joAaQR8CN@y_sw!jT9C&SJfrPRv15?Tw z9X=&mH=?>c;5~eQJPa`iwy2i=hZI4&7`mRufYw4dj`yJaKs?%yKYG;NjA~V=UAziqPo* zWp7~}O>_~f2|zV=uH$~dX3me~8x?I`vg2EXKUpcprlO_gfhi^oaSWAc@2IFg1UJu! z#*Z&BC4>Sy9*kR~A3)RV#?eLuTmKuA3P-&ClGR>mOcu2#hY@?%F`mc-^)C zfkAh@!P=gdD@K@V--~E(IPU1UD9&Q8So-_-??y3p9(qSas62``0uFBcpIU4~=O)!L z;$nxBS=4X-mWC%=JpssQ66D+`Gnhu-Ld_}(XNl&@9ID@qZxQF_W+~;#*i^;*>Ghq@ zCoK0v_#BeL>{?JyPa(I^v!k~yp}6R|ySLRTg=`cZJNE?+;Cbn}wVhJv?V&^^Z5WR! zCgB$$ZmS52dt)%eK{eW6Lz5I%AAB`C_QK^hLf zhPwDE;ZnaaIWa5@I)mG(u=Dk_5-qF%D8)ebRukKkX9lzKLyYJZv=CP8J1(s3?Bq4w z3nyqToc*C4Gjg$_lBDxxl1izA0{R&peqgYrGl4FG|iVZtlL@%k^k} z0JHVRI=J&nWzyIABtr!=y_PZ|BBh(`MXhpw=_`idzOL(Eu`Y8^nz`m=sivRS)n{BO z2lCa1Vl($3VKq@S5QuZDFA}hvyxDfnazi8A7SnHuOG@hUzSsf&qc;cy-0{jq60DBf zy@}(l)yhA9Ks-D=n9KG#q5o?y?oVdOB!N9XKQC@>PB|D)V|O`}+w^YR* zt|&nx5lC%y`~CMQySj3ah2G=;IrQWVMqwvSTF@qzz3iOb*n}Qjx_7><6O>Llzic8E ztvaEc`(9IYw+!3!J74AKd2YTiWb~jkFo~5mEqN>dMvRKyqnjVe zYhQj|{tzoxqLHfC!az-kjYV1oDcIhQA#HfZ_bqIG0NYBTlhjwdI7T=#a3pWBjw> zt_ycx7m>K~O6qde5#i6PXvSb?5Rhx<%>8=m(Ryj_S%~;fmseTSmMXOM1o(H+M4z$N zo^si-pY!1XE@soD2#W&fABwgZBX-y=&dZG@RO**i9uTm*YpXTKYINbrWYb>Lm$wyN zv+N!Z5BQ|VFm+yB^sJf2Ez2HS_E^s~>Dx}Jf)&u7ly`K^~~jw~&+IS)vQLVdyhYpbmbJd)29=gI4I0{$TV4yCrx zw|&eYP*sLvpe}ESUmJ zHSE@U*?u-+(TIT08U0@!4u!`~E&7d4CAZxW#aysSx+&5a1Bj}jJ5Bp6L!(+aL%ke=r|24W)bDKnj025bTiJNE$Rj)lcbj(vxdR?bQ(3-wR<{fn5$+kOFF21uq*0V?f?K z==Y4KKP*9!L?VDt2MyQOcPLc`icEW35xE!s2mY)1jiZ2c8NS6{Fo<7b0I?8BBCn9M znvNP@x%-RxxDRT=Xfg;hkTQ2u^)xc%)r7<%h7m)Fz$g}m07pG2&=iiFiv8UkkyXIP zmY#x0Jw`cVV5peY`;3~K4{Sh*GWErwE5~!B5T~{~jIm`MZrP=m3U00hTR)oa;NOaw z**Y`Kyb!Iv>#TTFb2HfXGZMPTT1H=R7&wwA(*?Z6-aivGrWQ;-172pzv@~VGa)ySm zl+UGgEjiN+9l^hq#T;XB+Wecomz1!KR`Pl6-Ys8aEi~0YIWb8|{Q1%^bV@8r%5Xas z)Z${}fZ3E@MH!HID4$CmMj8yw=DmMC(1nj_wDixc7$}7>RP+6JJ0zz8uEFzft7=Wy z-xEHv*O++pLD=6qABdibT8VI{Ef;_ZG$q&kaa>!zE-A~EkUUzAVR+NnA)DT3j3iEH zPsB5EJN;n*)R3GyBPrnc_GgiAIXU9Er3TQ&TbqlpO__P4Xud=lJkHqqbWIAFD@<&@t|vIoB#ZI!YEwY=Hpd9NQTlK_%pa4Em?MKmPOTE3@`G) z5Xn)}tirHH3Z-0}e5he09z2KRq$2GyuI?^Vm8vET7y38Aob(XWBUq((NGFRbxq>2x zK;=)a1U<6|TN=Mz{651P2Cza39Ywy@>+5^dp~W`bd&5Z!F`c+Fy@@XalhdeWh7LSs zlEiNcGo6MMO@iv`>Tj`%AtbPZrta1HI-4!0Aw<)6Xk{93QPcl`Wk z4LV>&GfTTT((c+&fyZXio$#zeFy zB~Jz@6P9JF>)EZUVFT--CHa0@sKeFBeE=vgn#-YHIfUI}(K<%|N#CwG1KX+GB@cWTq^aK|`o zCIgsIehZ~i%-uBJZSAG_rxO_3?M4DEf+}WQ3xmUHyF#a$B^JWta!BD=#ewMm1ygQY z@pH0DyHVq{i?uZ5K$2D*Ck!KMa4x*>&T(#id^2`>0lhUfrkEplmT2Xvf|)Q&tOt=0 z5#sd+0a&CcPF-sW%qze9`HDfo*4VckR#S)oL@iMkDd02g#+F zgrY8o6JXsdaE1{w-eXNa=f|cS$<#}2phSebQ7Fc`t*))>c*C{IL;6iJMvxcKwA8>(arjIuexvRS6|0qIaw9s`xZ=!XLmNKxsi72@A`1zht5YBN%AvM;wy9$yDEBN5w)~EXm$2|oIinhC! zFdQ(mF&cJfgOqT>{A^^DFj8Zj2GHKW+b;c(2r6=&T}h%Slm|B>#oo1Fu@5k)@G3<8V@J( zF3)<4MzmwAJplorshMtIr%xNd0RgewiKYNi z52AHzwucxiA=yIC<0iEPXd8NA5}ULo0_3_N6rIYt78nGyvwhfJUYr9}b`$RVpMf;s zA+pR}O`Q#-tT5ApZ|7#Bs)xEZAld#u@&IzNzSvC;%CdVg<(3+!1@UbdYtG7g(?fp# zCzFE^*l|jm%a}B^DLmA51&ONU-pGT~2g8`mAm^8|QXO+S5(%71O0p8|AHxQ44RqSP zbKbMQ(k#)fPtP`9-nEYB9oFHw)kP!<(k%zSGu`Zf&B2AG$G&1W7ny^D|Lo+*L6cpv z7x30FdXL(F_8rv-PjEqjG)OZw{ZCXE!;1di&_f}uxM@=+zoM8slCcfENni4HVcJkV zA%@U1&BE#PZZLDc$!vT}jAXSrI@YZOH~OVSsRba%tIsU&0JcC^Q7vfeydjv)_`?YQ zfKbLk9gG|%@=wlk=)f(Id=t8Pp67lEVZZ6J&3IA9xB;=^R8t6@lWD_m-@lP#G3)%D zkL`K?f*m{T49OjNCRY`p#_5qI9c7r!TWC-Tt&FcE+!Ps>Gpj zMLcG3-(*W*(L$}v-TMq{_sKjbH>E<7h}5K>vP90o5&?s;Vq(!_I5je3~xG8HMIbDWYI`$)SB}bBiNiQ~JIdt>fp!$O8_4CSG z_#S4QbiUt?Ln_z5cl}@GlmsN5h<%Ou2>W!Vv1VwbGYn_v!kyjiDW6H zCJK?P`|0D#W%|QINqsLZ0q#xll?en=abSo%N$~AkNy#N+%?>9rnwpw?FR=iD-w>Tp z?ykw>lr%MmCGey7viL403_pGGRZG`}tIw1!Jex9rX`B9O7cK8}FM}6KC?ofw58Y$i zwdehFBZJ2(=JSwp5C*tt-B@+RmBr0)u)lJ7eNrI_qg%D4h2xf}7R6E}c^+NsTP&T z1RRlW0nSvmolp!{VYY@?oXyucACE^*0qLR)tRiX*;lCu~h$gXgZTq~>s?&xhLb+po z0x`ctRr?AKdEUck5K%o&h{J~5IW1p-&uEw*Xl{WXY3Qpe=9W-}!PtOEHQA=tRnBwg=w-xciM{9QZ1cS#)CTYIHS+M z;uD!1QNO3<#EWL=M7Z(s_pH)JN*};tAPfW$RukPGxpSu0wK7w6cjY*wqRABQDJ!J{ z^Y5QM6UT%MT`B7pkYS>^fzthbI;fQP@FB(UFd7Bb!Lvwf*8G*TGV?VAg%x-bM1C0Z zXAZ*8PXWChOvizBGojm5tvskrNg}l3ta2@TQ1Dncjd^+fi~*fFuVK~{3N5>dhc`b% zg}+IQ$Y`b{;?O{|VjDW+GIze$=9YP|)BIY2B!LdiT6s1=T(P#ok-tFND!YO!_t(T||)qWW0UROmYUGBE_q!udP;{pAhRQ z@T#GhKeDu5Ts=83PD6(rE<(BN=`+80Qg(|1hFciIdydTMQ%mJA&s?s7``%nO6fp^$tJrBQIetV?yK5xX)yy6_i0hb5@ zuAA$a^+YGWuRtshjHr)347Y|sz7`-ks|1@g4x>9t$(R?>+Wmd)vuM_@1AJ9?Oye<3 zSzL`ydg)7_{Xlgr#{?}{bpd$wXuj6Su&(fiKBl*N+eh2XOe{1;!`$qoTo@hIjSZR4 z1fd_2@FqKoTwf7RXK{z=rg6Ow74x2}cw&<>4d@{Q8lK1X)GJ?s!HJ920PAaaL14Ls z+U--Qy%OG^(#)_hL9>e2y~Wtl(S@0VMJn;y!9v?;2a{~Etq-ZVZrH90;p&S!VnGPP z{96ljJ9QNG22-Ce`NH+m(yUpduHxUORKW|at`$zAvZEdk90k5xN3IRhB(sXrf8??$+n0ulLvk63^krpn8O&H-4f1 z3&=CszyZJwimr-&izdyF99dGqfMJWcm9j;#VNEpp5~pUgLX)%lwl_21RNUl}8asVU zP(ztKlL_BzZ7(LHpAl~m!_6SToNU#ke9&Ttk<;`qQ}R!e{8~HQN|>NIGr4eIt>v+O z_-um_Pp2bSlShMFCJ=7lOI;`@msx}OzX4PNg)!hREuk#z<>rGwhS!+EU z)?W{g++pTzr2F!T3uN|4PLNgd_;P`42595Jd28ZAK-|nIS}B5iy?I@@TK1iZv^#*_ zyqwV2Sfqg7fK%X)t>Uj>1IjGf08JSR>5K}f^pf6*VI91JrF)>mL60b-Kg( z-mon`yrm6-Qy4(x;}WO)KQ|Aex1QnSDUY4jsh$)qyfEX{Ki5N94lGW)AiE(4Mgehv z=zGm{odB|>mpz47*)Ju_Z*Hl=N3iOc1vRDRE(2qVqC3Ur4klT`J+jla1!kn`?sU>; zg2i4~iBq$Yo`KD#mo;sGs`wlo@A&;K^}H^!M2z?bUh!@!KG7<2(ORilS|c@7H4y$8 zwkFuy)xyqsd|Mpq14@#nkqa4d;}d!zA#6c6KnQmT)Gn!SFj9^5 z09O;)qj8D?F+7c%)Ns6s(zOP0zkKq5gq^s9>m;HVX&)S2bQYBZTgDMHOWQ+1=(<2i zIY<@AND^h{VeM)DQVxI&U&2L9v!3q#YWTd&T<#K7VB(J-Pk6=6kZ5sQyuVBm@20uS zn=*>9Vj7snsJeXN3bju6t?*j~`;mkQCG6qa!OBbBW=&#w->oitzWm$U+XZh}jj9i- z|7`9CLLeVTSmqY6H)z#j;YrMRKI5+Jpj$OJyZ%F1Y8P<#8THYupFeR%WsFHIj)~zk zM|zqAxV_w^+(~N3)!bM38$;!@(H2MRrbW^jY5-zb69WYX#C5UF{a9AbqGALFC@iZ{ zQ!p|`O`Yblxl<_e@p`QzmyGMPihTB=zY~3$Y8}z%pVn#Vr2TOQg@c{`^OR3z-<2^S z!RAZl{|jQ74Nd%EDTTG?K-j=>7Xa3f?pclAQ@VqUn4#^`{s@ube6mm^Ooj7b4-1!mB=-9b zO0LL7s#oX$8`Z9wgdqNG=7>I!I9Aq|FQ~3GvNcmg)5Rp7X-H*4$c)Mxph3QP5_bAd zqID+5(GEYozzHK$as%G#S1VH8)V)neXn zM9$uQ!i^D8Twb&J7w&qukd>!GhiEL9zrm9N==BX`K0MWV)H~I%%NK$kipq~1>|WUE z6O~bDr5*21jQ{P2d4SV$SaDIMyX1 z!^o({Cr9ZmTaY zaw3y6WlU)X1R-xCx~Fx<|GPB-HXQJF!h27>qH!@jNIJBwtf&=J-M{m;kmp-vJ`I}w%gom(>_r@ogNcUky%SIP=NeUpNVdusfUdE2` z3vXKF06rb(nhlf5NJP32zO}H9^C!ENIvh2#jS8mxv6>>ya=rA-K;dle-y!iPj1e(A zH8=PCEZtec@%UY|)UF%wnb`cBrs0#aJcT?#KfxQ9#ib?Zb*Evc38!IdzAo&Un<2k@ zC%%&r%aKPyk+1Ilx;6>8-I;{AYwJqjE}8U^9GYbcw^m}K&NpeNs@otX0eA>u@bA2| ze@q*>e|C#d2or2Slg^66tGk#UKVgl$GWN**9`&;q11*iJfk*CUp6~-g4trJ14JjS3 zy%>6FHDpIO0Nr7G2;({;Dky6d4dz7*rdJx5iBe3{Jh11lD@{RQ_IIsADi<{!qZa(F z(LKluxfPP(y3Yvq5hs0<2yIoJMsi^=Tr2oU2K~*c*4ch;6QwYDn5yACgN#9jUtS4{ ze~R^+#S*bA4Po?S|X zduq*E?Ujzy6dO8ft1=QpHQcgC!h3#8r%L%&(w7n_(Gx7vW_~W41mgDYrmUL9=p@~1L+7RoM58mgZ+NPS^u+*AlJ7iR9pXI-z{uq z5-m!G+$?VZdZD!|~Ja#`qGH-j;vMh~wnX-*ilLVGANAWAe){od4+E(1MPej@}H$`@@A~(gb zZM=l(M#XP?4x0wd5Kc`Rf6)T>&DV~=$5se66(b z)D`w(=GMTX__Z|#!km)sKziG+KsKg1B2`sgaZN$d)$~sVF~5HV78@UY>F&(ONU)-> z90-RC=nAC;h7f=c>(dob7)#5e=mA|=2FgXKkDWSzzRu{9pU8jTm^al;DOwaXmu z#MMP?%=SB%h{lT_`4hWMEyNQ#npCSGm^K9K*z4vS&HD2+{h%bxz$8hYv+wCcTIM&0 z4WVwK${ChT5+~DvGh-=*1ZyE9`&N1%QkKKm&2aV*=`c}$Ipa{n)iuHy;%a>O*5&SG zDtHuc2kR<<@*nYnc_nLyLu!J${nLtgoF?@J*$}TUOpA>>AJ02lr5uznPrtM0vz!3q z-B>;?!O#P{UZ{Kyz$k-!|`z4$wZiLQJk>IoJ8WG?8zTOyq8YDX&#SCZsgIfIO zOYG5mI8c$BSz!&wNySjAR~Yny=@jtS9+puj{N7D;3D+v4=??n5nKHU6sLiG0c{n&= zk1Ne^1mC9Rjc0RF7>p&;;mf;oW(gdnHi|%GJOoAS3t zw{L??(zYXa#`E&S?5-<-R3^J2$Q+t2kwYPA!L6~GtGscQ ze*+xzycpe{`CkH%J>Y z33?$I;==@28jWVR5qJ!c>iM5QO&GQU(B1Yd)p&NWyj5VO#z#O=eut1jt zVus}A6qc5T2|TW4kq3B!>-xFr-u9m9UtdokSuh}bRu*N|*m9WJpus2;kQM^(Q2i)N zEtYI1I?f5Ke2@p(UbzxWNB^EmTga>p&?LbTX+4uLiT$R`peLOLo!27 zTd!dteX>e*-eJc0!uonN(9;RSfrd;oO3Fa2xT{N_$orUd)w{A*ib<2P`|9bG$$+P6 z>0k>PmrV?b_n0Ju%OTe_azqccty+FRADho?Aa5&C@M$V-9fC2jEg*yY$pbdvEPkkm zArZA~i)x<}tvrP;J}=L$R7({l!6+29b_KoDr0TteW0a*{ihzb^g#{}jT8for zPCuFwR&zGAljL?hPcQTNN0)Vhb@u(sCF0z5QD`EMv>2fsrArXdqXOt*!myb@JhsAa zG5Qi0)mf)z=Eac64C#DvRg(sC?JM{#4LHeTk0RU&m1D6E$i^-NYP0O?+ukoe-^{!u zeQu?e+@nVJP3pO)bk4XqII2^7DmTEem<{wa7(fv%%F$QpM7%c><_h;op>6bteV zlEtI2QsBu&6}8jks6dJ7F8}3PLm_|fDQ1u!9$V-x91DK^=MDF2RtYm$R)i(PYwKWP(c*kJ8Z!_~<)c^A}?ScvM2%aPr3oaS>n+d$tG@ za}~}yn7+xg5!~byROx0FLw2)$@Gw0etn%MY+`|p1u{Ef-5wX~O6RE}k1wao&&=s}^ z&;!Km)N=s8fWB5S0#Ke!UQUh4$-Rr>!kY8|*;EG7NHX88VK@Ckn!@_}`XL>k_@!4X zn+vRxv6Uoh#Y2z{Q`XkjmIkPjDoLf)>5yA@B7-6Frs(iH>JWQis>R${g}RBSCcbH0@-5X8Jo)>fDmN3alN!4X)Iu+6rtKEmpqHp0jRSb!QcEmgg8NYh*RrR zFawxUKme%iDQeS)i^b2-0`>*h8sBGQ&8iMu&ps9=Ae~*5yA-A7vruwZ@q;+7K zzw1#_kU}FF<(`u}U3Krp7>`+wT8^~<5V0f6$O&Ndi_N2L>)(pMn93wYocgy?HQX*96x}Jul2jAysZuBqA^}7U3LcPsmC=7CjTue{!)|{yi791z)99{$1Uiul=VyT2EX@KYuIadL<9<4Yx9f; zhVGRgjeief`t#xqK_@$cYrdZo{g>~(zrS1GAcL*Jz}%F{U|}sF_*f+?eE@pHbKTSb zzKGcAxBzCa9VvE?8KxBc=)cUnKd9M5p1dk|;xt@mGCS+33LvN@d@qi5QOy7?$b~#S||B?TBSdG zJ-?TV((6K!l2&@0Gzp{3V5O2_l2mp|xrcCKA0OcGg(#i0^_aQ8RXV`Gl4Y}r5m$B2=cV`*U3f%uv zE7M+^Nmo8%SyzBtH-JbdAWdu2^08W>HQ3l-<1m4cD~%N=`MWfP>RRxPP9fKcAVtMe z22pbUDm-i)Ct+G$!T=^aiYCO>1-jc7Q=QSQJE#x8JZX1E)j?GRj)5bOJBYvBc{_z7 zcq9#CeMmZk5whahuU((rci)|Be|t5fhK~Ti#HOPVSk!IaL`_3*?K(~Qx8=V z@zjHNa^tm=dF=6L$!6*)23@R}H&JRLQL>QM9lIVETzDa0-C+lY3>r)j1c8YBEt}0= ztCV^bDDn~Xg%$vS=bwL`Y&OgI@#FV!9OpW*GQ@EmCyF9<&N=6B)m2yFI7thcRprR} zzKe-sL?D;TP}^?+=U#Rtdw%b`C=UYQiP><|Q?vLms-o4_5;@hlMkk2fvf@@MSz7_$ zwHhb(lalu7p9u7XpiScKE2$2SgjB~Z^a&QR&x9cyw(n4e)FfN`?DyW|XFooX#~*qW zlOAbRxq7+_ofQIL$>nn845eHy$C1Yz!MW$2N56)C5=2Fl@v_ap$-uqOKKrbc%FBQ0 z1)#gTo9(yXzTR4UoLD)`ah(mUwY>W3t4y9enY-?~i}v>Rm6|N{5(cI5G8yv40(*XY zKMpzKNWM04Cv2U^>s|2Bt3{rEe<_PPJYro#tg-~6leH(HtU*kiUKfCB^Qt5WL7%! zzr?`-AZd+Q!EzMlqpk~GGz+yz4JCf5G4p4l?GHQC_~bU zSUfPIxbU1`GUo4G+aO8pvJM6pDV2Ru z7K$DC#Ww0x%z(NKU*BXTN9+RYWx#a9GfzFvS-+;pkR<;pIL|5^d?JdZF8@qM3c zHp@mEZIlD{F|j$$ah)BlwS4&Dhy39Wf8dVWZesb8W{Lq69eSr>r{thWT2aELn{3Ll zKRT6N_L)SV#tgGM;I;X1=hQ{K*Imo}f}0k00ypkP5fW)h@OxAz*A3dK@(K)b+t_Wh zk?b_Kn(fCVM#=L}|BIV$yq2e*dX(0t#kkoVDl`~ktZS1jP9r#4V~Q3Z7%*@UhaPn# zhaP$;Lx&D6nat0L$n{$5d!{`*t-DkNv#P3!pu2Y=i~lMCD4pxZn6lzGWy%zcG2C|B zZ8;G+xKJou?l?}p>$tQmp2rW5`~go)eV)#U)Rsy*Xq3(cg@@7#R||0*k;!;$z4f;I z?z$TozW(~;J@_z&o1TIPUz$&w)DbHm*F=CNa7-HK^-wk>Q&__0BdR&=py6y>1N|-Z z(Qwi!XEJ5V4Fvi2im6LIi$;tB9G?@0;f=X$ zu6X28N6O(x9)|0A#A&?6bNpV;oX=VSK6hHNw5*71BqDc;$Wjp_BC*_e@13&swp)vv z%Oo@H>LjTk^_wf@h~qdb#T|Zp=GpSf8z0D`P-M1<+_hX}@4qaO^?toj>VC0AsxK%= z=E5#<&S{c9znCZ6{cef;a=OUBtVoN9yuGMX{&>edvhI2t3TVmXs#c02_+B>oS*OEu zWaCXYm78vyl6dTm6>IG>Ywf+(+D((1|2^aXds>A6AR<{2*-b093*<2P7e&OYa7@^*8Jw1~(HA~JcF$hRgp%kZDf zkO4oLA?y5Nh8+L*NdD0w(j+1sB68PV56i?IzAnD+CqX99`{+Oth~qf2?z-#BZ+`O| znLT@UDPR*>Yag}Ne%o4`Tao$y697I^04m3R*4kxp6iXb%($dl*M;viPFR%#}LO?1Q zzLOD8`{Lq=gO-gq-CUl0;YDc|ktLEadh(NV$zWZ+CC>|&x7mLV;aTJTS zR-St5DcNVAeWbCmv6m2(R=)=L z(Bk3WwLg$R9*x<3gd}-FDk;+;-b-Uw8rd+}{`%{==%R~Qx^yWbLN4PGhEbZ7sSuS(P$5=GVse^(BZlI7n%2e5 zn65ndie9N|X`ZU*DU@;$QS|NGmy=F9i4#vek*ZvkIIa_l$ZX(@d+)vX?{OTLtsQGD z`%l{cW1sJnWGsJ{^raAhIF8FOvT}#feZEucxXvNK;lPA240-?k_qpYkTe$u9+gY+? z39*Ti_)oHtljIqeibfPkA_)~U;isluWr#lgw_m>`$@Iu0k7R=lHYoKCm|?B`lTzyD zJO6U$^4P>^om4+pTH(4b`|i8%$3EZZEdZZ?+pX1*h~Rl%*>_OFCEWA3dpd#Zfn9+! zMWkcw*s)xA;f36P|NU&c!}bZUleFBP`s-*Q1f|fq#3@9z#;Hj`BQX`F&y#<)-+p@@ zeDFaoxZr}MdWh?F0>3b^*;#9S&As>DyF4~AnM@`rFcpQLJ7fR9p<=OEYR$f-G3Id* zX%`V286%T#oGj~XI7TuxsjZG9nQWanUQQ~i?$ zOI1}>*<4ggNmWfv*}gAn`u+O#lM64rP(JwJ11VV@#bWUpW6VJ&igQshpx9lY*uDC~ zlmDa7m?{Op7(;7oD z?kFdUNUl&Qs1oDh=?yD+XX>zyNDEnAdrO%7s{=--YOe!ys>OJew^HX#~t$StoJ1rE7sbuySw}D zFbsDT5x=`DPj^?ILVjiD7rP7PI!PbZOoOj-Nt=8JhfL(x#B4aL(<7g)l sx#;R^uWf2;YfC(dxm>yG#(y*YKaSaKs}}NMb^rhX07*qoM6N<$f>MXKJOBUy literal 8055 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 literal 7725 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%0A+z-{=B~rQga< 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 M07*qoM6N<$f)ka5R{#J2 From fa7a6ac69056f927ab9af43f2af8349aca12fa83 Mon Sep 17 00:00:00 2001 From: Jouca Date: Mon, 3 Aug 2026 14:37:21 +0200 Subject: [PATCH 32/32] update(changelog): finalize version 1.6.0 and update mod.json version --- changelog.md | 26 +++++++++++++++++++++----- mod.json | 2 +- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/changelog.md b/changelog.md index 2f4ec72..80c1390 100644 --- a/changelog.md +++ b/changelog.md @@ -1,22 +1,38 @@ # Changelog -## v1.6.0-beta.5 (2026-06-30) +## v1.6.0 (2026-08-03) +* GDUtils ported & rewritten for Geode v5 (GD 2.2081) +* Added 2025 GD Awards badge. +* Changed logic for chest notifications. +* Changed locations for where GDUtils stores files. +* Adjusted default parameters for the demon list to be AreDL +* Updated UL staff badges textures. +* Fixed Spotify feature not unmuting when playtesting in Editor. +* Fixed notification being clickable. +* Fixed chests crashing upon opening for Android. +* Fixed daily chests being ignore on claiming. +* Fixed GDUtils badges crashing when clicking to view details. +* Fixed daily chests crashing. +* Fixed AreDL demon placement not working. +* Fixed notifications queue potentially staying open if the connection didn't close properly. + +## v1.6.0-beta.5 (2026-06-30) * Adjusted default parameters for the demon list to be AreDL * Fixed AreDL demon placement not working. -## v1.6.0-beta.4 (2026-05-13) +## v1.6.0-beta.4 (2026-05-13) * Added 2025 GD Awards badge. * Fixed daily chests crashing. -## v1.6.0-beta.3 (2026-04-24) +## v1.6.0-beta.3 (2026-04-24) * Added back leaderboards & moderator list. * Added back custom backgrounds. * Fixed daily chests being ignore on claiming. * Fixed GDUtils badges crashing when clicking to view details. -## v1.6.0-beta.2 (2026-03-08) +## v1.6.0-beta.2 (2026-03-08) * Fixed chests crashing upon opening for Android. -## v1.6.0-beta.1 (2026-03-08) +## v1.6.0-beta.1 (2026-03-08) * GDUtils ported to Geode 5.0.0 (GD 2.2081) * Changed logic for chest notifications. * Changed locations for where GDUtils stores files. diff --git a/mod.json b/mod.json index a0164e5..f4170f8 100644 --- a/mod.json +++ b/mod.json @@ -1,6 +1,6 @@ { "geode": "5.8.2", - "version": "v1.6.0-beta.6", + "version": "v1.6.0", "gd": { "win": "2.2081", "mac": "2.2081",