diff --git a/changelog.md b/changelog.md index 5d840a57..f4466fb1 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,8 @@ +# 1.7.6 + +- Fixed lag (thanks @prevter) +- Added **Label Import / Export to file buttons** + # 1.7.5 - Fixed December Menu Snow not working diff --git a/mod.json b/mod.json index 6a3678b6..57313f16 100644 --- a/mod.json +++ b/mod.json @@ -1,6 +1,6 @@ { "geode": "4.0.1", - "version": "v1.7.5", + "version": "v1.7.6", "gd": { "win": "2.2074", "android": "2.2074", diff --git a/src/Client/Windows/IconEffects.cpp b/src/Client/Windows/IconEffects.cpp index fe229080..4e1cd844 100644 --- a/src/Client/Windows/IconEffects.cpp +++ b/src/Client/Windows/IconEffects.cpp @@ -6,7 +6,7 @@ ccColor3B EffectUI::getColourForSelected(int mode, bool player2) // bri`ish int sel = 0; float v = ColourUtility::va; - v *= Mod::get()->getSavedValue(fmt::format("icon-effect-speed_{}", mode), 1); + v *= speeds[mode]; switch (mode) { @@ -30,30 +30,23 @@ ccColor3B EffectUI::getColourForSelected(int mode, bool player2) // bri`ish break; } - if (mode == 0) - { - if (sel == 0) - return GameManager::get()->colorForIdx(GameManager::get()->m_playerColor.value()); - } - else if (mode == 1) - { - if (sel == 0) - return GameManager::get()->colorForIdx(GameManager::get()->m_playerColor2.value()); - } - else if (mode == 2) - { - if (sel == 0) - return GameManager::get()->colorForIdx(GameManager::get()->m_playerGlowColor.value()); - } - else if (mode == 3) - { - if (sel == 0) - return GameManager::get()->colorForIdx(Mod::get()->getSavedValue("same-dual") ? GameManager::get()->m_playerColor2.value() : (player2 ? GameManager::get()->m_playerColor.value() : GameManager::get()->m_playerColor2.value())); - } - else + auto gameManager = GameManager::get(); + + if (sel == 0) { - if (sel == 0) - return GameManager::get()->colorForIdx(Mod::get()->getSavedValue("same-dual") ? GameManager::get()->m_playerColor.value() : (player2 ? GameManager::get()->m_playerColor2.value() : GameManager::get()->m_playerColor.value())); + switch (mode) + { + case 0: + return gameManager->colorForIdx(gameManager->m_playerColor.value()); + case 1: + return gameManager->colorForIdx(gameManager->m_playerColor2.value()); + case 2: + return gameManager->colorForIdx(gameManager->m_playerGlowColor.value()); + case 3: + return gameManager->colorForIdx(sameDual ? gameManager->m_playerColor2.value() : (player2 ? gameManager->m_playerColor.value() : gameManager->m_playerColor2.value())); + default: + return gameManager->colorForIdx(sameDual ? gameManager->m_playerColor.value() : (player2 ? gameManager->m_playerColor2.value() : gameManager->m_playerColor.value())); + } } if (sel == 1) @@ -64,16 +57,11 @@ ccColor3B EffectUI::getColourForSelected(int mode, bool player2) // bri`ish if (sel == 3) { - std::stringstream fadeIn; - fadeIn << "fadeColour1"; - fadeIn << mode; - - std::stringstream fadeOut; - fadeOut << "fadeColour2"; - fadeOut << mode; + auto fadeIn = fmt::format("fadeColour1{}", mode); + auto fadeOut = fmt::format("fadeColour2{}", mode); - ccColor3B in = Mod::get()->getSavedValue(fadeIn.str(), {0, 0, 0}); - ccColor3B out = Mod::get()->getSavedValue(fadeOut.str(), {255, 255, 255}); + ccColor3B in = Mod::get()->getSavedValue(fadeIn, {0, 0, 0}); + ccColor3B out = Mod::get()->getSavedValue(fadeOut, {255, 255, 255}); return ColourUtility::lerpColour(in, out, (sinf(v * 3) + 1) / 2); //fade @@ -87,7 +75,7 @@ ccColor3B EffectUI::getColourForSelected(int mode, bool player2) // bri`ish return {0, 0, 0}; } -std::vector mods = +constexpr std::array mods = { "rooot.custom-gamemode-colors", "gdemerald.custom_icon_colors", @@ -139,16 +127,27 @@ void EffectUI::updateValues() glow = Mod::get()->getSavedValue(fmt::format("selColour{}", 2), 0); trail = Mod::get()->getSavedValue(fmt::format("selColour{}", 3), 0); waveTrail = Mod::get()->getSavedValue(fmt::format("selColour{}", 4), 0); + + sameDual = Mod::get()->getSavedValue("same-dual"); + + // cache values for performance + for (int i = 0; i < 5; i++) + { + speeds[i] = Mod::get()->getSavedValue(fmt::format("icon-effect-speed_{}", i), 1); + } } class $modify (GJBaseGameLayer) { virtual void update(float p0) { + auto plr1 = EffectUI::getColourForSelected(0); + auto plr2 = EffectUI::getColourForSelected(1); + if (m_player1) { - m_player1->setColor(EffectUI::getColourForSelected(0)); - m_player1->setSecondColor(EffectUI::getColourForSelected(1)); + m_player1->setColor(plr1); + m_player1->setSecondColor(plr2); m_player1->m_glowColor = EffectUI::getColourForSelected(2); m_player1->updateGlowColor(); m_player1->m_regularTrail->setColor(EffectUI::getColourForSelected(3)); @@ -157,7 +156,7 @@ class $modify (GJBaseGameLayer) if (m_player2) { - if (!Mod::get()->getSavedValue("same-dual")) + if (!EffectUI::sameDual) { m_player2->setColor(EffectUI::getColourForSelected(1, true)); m_player2->setSecondColor(EffectUI::getColourForSelected(0, true)); @@ -176,9 +175,6 @@ class $modify (GJBaseGameLayer) m_player2->m_waveTrail->setColor(EffectUI::getColourForSelected(4, true)); } - auto plr1 = EffectUI::getColourForSelected(0, false); - auto plr2 = EffectUI::getColourForSelected(1, false); - if (m_effectManager) { if (auto action = m_effectManager->getColorAction(1005)) @@ -216,6 +212,4 @@ class $modify (MenuLayer) $execute { EffectUI::updateValues(); - - log::info("b"); }; \ No newline at end of file diff --git a/src/Client/Windows/IconEffects.hpp b/src/Client/Windows/IconEffects.hpp index 95a1240f..f7b54ac0 100644 --- a/src/Client/Windows/IconEffects.hpp +++ b/src/Client/Windows/IconEffects.hpp @@ -20,6 +20,9 @@ class EffectUI : public CCNode static inline int trail = 0; static inline int waveTrail = 0; + static inline std::array speeds = {1, 1, 1, 1, 1}; + static inline bool sameDual = false; + static inline Hook* _hook = nullptr; static bool getIncompatibleModLoaded(); @@ -44,30 +47,34 @@ class EffectUI : public CCNode void update(float delta) { + auto color1 = getColourForSelected(0); + auto color2 = getColourForSelected(1); + auto glow = getColourForSelected(2); + for (size_t i = 0; i < players.size(); i++) { - players[i]->setColor(getColourForSelected(0)); - players[i]->setSecondColor(getColourForSelected(1)); + players[i]->setColor(color1); + players[i]->setSecondColor(color2); - players[i]->enableCustomGlowColor(getColourForSelected(2)); + players[i]->enableCustomGlowColor(glow); players[i]->m_hasGlowOutline = GameManager::get()->m_playerGlow; players[i]->updateColors(); } for (size_t i = 0; i < players2.size(); i++) { - if (Mod::get()->getSavedValue("same-dual")) + if (sameDual) { - players2[i]->setColor(getColourForSelected(0)); - players2[i]->setSecondColor(getColourForSelected(1)); + players2[i]->setColor(color1); + players2[i]->setSecondColor(color2); } else { - players2[i]->setColor(getColourForSelected(1)); - players2[i]->setSecondColor(getColourForSelected(0)); + players2[i]->setColor(color2); + players2[i]->setSecondColor(color1); } - players2[i]->enableCustomGlowColor(getColourForSelected(2)); + players2[i]->enableCustomGlowColor(glow); players2[i]->m_hasGlowOutline = GameManager::get()->m_playerGlow; players2[i]->updateColors(); } @@ -90,16 +97,11 @@ class EffectUI : public CCNode for (size_t i = 0; i < fades.size(); i++) { - std::stringstream fadeIn; - fadeIn << "fadeColour1"; - fadeIn << i; - - std::stringstream fadeOut; - fadeOut << "fadeColour2"; - fadeOut << i; + auto fadeIn = fmt::format("fadeColour1{}", i); + auto fadeOut = fmt::format("fadeColour2{}", i); - ccColor3B in = Mod::get()->getSavedValue(fadeIn.str(), {0, 0, 0}); - ccColor3B out = Mod::get()->getSavedValue(fadeOut.str(), {255, 255, 255}); + ccColor3B in = Mod::get()->getSavedValue(fadeIn, {0, 0, 0}); + ccColor3B out = Mod::get()->getSavedValue(fadeOut, {255, 255, 255}); float v = ColourUtility::va; v *= Mod::get()->getSavedValue(fmt::format("icon-effect-speed_{}", i), 1); @@ -163,7 +165,9 @@ class IconEffects : public Window void changeDual(CCObject*) { - Mod::get()->setSavedValue("same-dual", !Mod::get()->getSavedValue("same-dual")); + bool sameDual = !Mod::get()->getSavedValue("same-dual"); + Mod::get()->setSavedValue("same-dual", sameDual); + EffectUI::sameDual = sameDual; } void updateSelections() diff --git a/src/Client/Windows/Labels.cpp b/src/Client/Windows/Labels.cpp index 16591bb2..f9f6cbcf 100644 --- a/src/Client/Windows/Labels.cpp +++ b/src/Client/Windows/Labels.cpp @@ -2,6 +2,8 @@ #include "../../Layers/EditLabelPopup.hpp" #include "../../Layers/EditSafeZonePopup.hpp" #include "../../Labels/LabelLayer.hpp" +#include "../AndroidUI.h" +#include "../../DragDrop.hpp" #define BUTTON_WIDTH 200 @@ -79,6 +81,12 @@ void Labels::cocosCreate(CCMenu* menu) safeBtn->setPosition(ccp(240, 15)); safeBtn->getNormalImage()->setScale(0.5f); + // ButtonSprite * create(const char *caption, int width, bool absolute, const char *font, const char *texture, float height, float scale) + auto importBtn = CCMenuItemSpriteExtra::create(ButtonSprite::create("Import From File", 100, false, "bigFont.fnt", "GJ_button_05.png", 30, 1.0f), this, menu_selector(Labels::onImportFromFile)); + importBtn->setPosition(safeBtn->getPosition() + ccp(55, 0)); + importBtn->getNormalImage()->setScale(0.7f); + safeZoneMenu->addChild(importBtn); + safeZoneMenu->addChild(safeBtn); menu->addChild(safeZoneMenu); @@ -576,4 +584,63 @@ void Labels::loadFromPrevSave() Labels* Labels::get() { return instance; +} + +void Labels::onImportFromFile(CCObject* sender) +{ + file::FilePickOptions options; + + file::FilePickOptions::Filter filter; + filter.description = "QOLMod Label"; + filter.files = { "*.qollbl" }; + + options.filters.push_back(filter); + + file::pickMany(options).listen([this](Result>* path) + { + if (path->isOk()) + { + auto paths = path->unwrap(); + + for (auto path : paths) + { + importFromFile(path); + } + } + }); +} + +void Labels::importFromFile(std::filesystem::path path) +{ + auto res = file::readJson(path); + + if (res.isOk()) + { + auto mod = LabelModule::createFromObject(res.unwrap()); + mod->name = fmt::format("{} ({})", mod->name, path.filename().string()); + + modules.push_back(mod); + + save(); + + if (AndroidUI::get()) + refreshList(); + + FLAlertLayer::create("Success!", "Successfully imported label!", "Yay!")->show(); + } + else + { + FLAlertLayer::create("Failure!", fmt::format("Failed to import label.\n{}", res.unwrapErr()), "OK")->show(); + } +} + +$on_mod(Loaded) +{ + DragDrop::get()->addListener("import-labels"_spr, [](std::vector paths) + { + for (auto path : paths) + { + Labels::get()->importFromFile(path); + } + }); } \ No newline at end of file diff --git a/src/Client/Windows/Labels.hpp b/src/Client/Windows/Labels.hpp index cab2c969..362499ca 100644 --- a/src/Client/Windows/Labels.hpp +++ b/src/Client/Windows/Labels.hpp @@ -40,4 +40,7 @@ class Labels : public Window void onMoveLabelUp(CCObject* sender); void onMoveLabelDown(CCObject* sender); void onToggleVisible(CCObject* sender); + void onImportFromFile(CCObject* sender); + + void importFromFile(std::filesystem::path path); }; \ No newline at end of file diff --git a/src/DragDrop.cpp b/src/DragDrop.cpp new file mode 100644 index 00000000..8e685bce --- /dev/null +++ b/src/DragDrop.cpp @@ -0,0 +1,162 @@ +#include "DragDrop.hpp" + +#ifdef GEODE_IS_WINDOWS + +// A much simpler way than whatever shit i was doing for that other winapi mod +HWND getWindowHandle() +{ + return WindowFromDC(wglGetCurrentDC()); +} + +class DropTarget : IDropTarget +{ + virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject){ return S_OK; } + + virtual ULONG STDMETHODCALLTYPE AddRef(void){ return 0; } + + virtual ULONG STDMETHODCALLTYPE Release(void){ return 0; } + + virtual HRESULT STDMETHODCALLTYPE DragEnter(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) + { + if(pdwEffect == NULL) + return E_INVALIDARG; + + *pdwEffect |= DROPEFFECT_COPY; + + return S_OK; + } + + virtual HRESULT STDMETHODCALLTYPE DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) + { + if(pdwEffect == NULL) + return E_INVALIDARG; + + *pdwEffect |= DROPEFFECT_COPY; + + return S_OK; + } + + virtual HRESULT STDMETHODCALLTYPE DragLeave(void) + { + return S_OK; + } + + virtual HRESULT STDMETHODCALLTYPE Drop(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) + { + if(pdwEffect == NULL) + return E_INVALIDARG; + + *pdwEffect |= DROPEFFECT_COPY; + + FORMATETC format; + STGMEDIUM medium; + format.cfFormat = CF_HDROP; + format.ptd = NULL; + format.dwAspect = DVASPECT_CONTENT; + format.lindex = -1; + format.tymed = TYMED_HGLOBAL; + medium.tymed = TYMED_HGLOBAL; + HRESULT res = pDataObj->GetData(&format, &medium); + HDROP drop = (HDROP)medium.hGlobal; + wchar_t* fileName = NULL; + //See https://docs.microsoft.com/en-us/windows/desktop/api/shellapi/nf-shellapi-dragqueryfilew + UINT filePathesCount = DragQueryFile(drop, 0xFFFFFFFF, NULL, 512);//If "0xFFFFFFFF" as the second parameter: return the count of files dropped + UINT longestFileNameLength = 0; + + std::vector paths; + + for(UINT i = 0; i < filePathesCount; ++i) + { + //If NULL as the third parameter: return the length of the path, not counting the trailing '0' + UINT fileNameLength = DragQueryFile(drop, i, NULL, 512) + 1; + if(fileNameLength > longestFileNameLength) + { + if(fileName != NULL) + { + free(fileName); + } + longestFileNameLength = fileNameLength; + fileName = (wchar_t*)malloc(longestFileNameLength * sizeof(*fileName)); + } + DragQueryFileW(drop, i, fileName, fileNameLength); + + auto wstr = std::wstring(fileName); + auto str = std::string(wstr.begin(), wstr.end()); + + paths.push_back(str); + } + if(fileName != NULL) + { + free(fileName); + } + ReleaseStgMedium(&medium); + + DragDrop::get()->invoke(paths); + + return S_OK; + } +}; + +DropTarget* target; + +void init() +{ + OleInitialize(NULL); + + target = new DropTarget(); + + RegisterDragDrop(getWindowHandle(), (IDropTarget*)(target)); +} + +DragDrop::DragDrop() +{ + if (CCApplication::get()) + { + init(); + } + else + { + Loader::get()->queueInMainThread([this] + { + init(); + }); + } +} + +#else + +DragDrop::DragDrop() +{ + +} + +#endif + +DragDrop* DragDrop::get() +{ + static DragDrop* instance = nullptr; + + if (!instance) + instance = new DragDrop(); + + return instance; +} + +void DragDrop::addListener(std::string id, DragDropCallback callback) +{ + callbacks.emplace(id, callback); +} + +void DragDrop::removeListener(std::string id) +{ + if (callbacks.contains(id)) + callbacks.erase(id); +} + +void DragDrop::invoke(std::vector paths) +{ + for (auto callback : callbacks) + { + callback.second(paths); + } +} \ No newline at end of file diff --git a/src/DragDrop.hpp b/src/DragDrop.hpp new file mode 100644 index 00000000..9a1cc946 --- /dev/null +++ b/src/DragDrop.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include + +using namespace geode::prelude; + +using DragDropCallback = std::function)>; + +class DragDrop +{ + private: + std::unordered_map callbacks; + + DragDrop(); + public: + static DragDrop* get(); + + void addListener(std::string id, DragDropCallback callback); + void removeListener(std::string id); + + void invoke(std::vector paths); +}; \ No newline at end of file diff --git a/src/Labels/LabelModule.cpp b/src/Labels/LabelModule.cpp index 6cad3a62..e4a8fc08 100644 --- a/src/Labels/LabelModule.cpp +++ b/src/Labels/LabelModule.cpp @@ -157,4 +157,37 @@ LabelModule* LabelModule::createFromObject(matjson::Value obj) } return mod; +} + +void LabelModule::exportToFile() +{ + auto object = saveToObject(); + + auto dump = object.dump(); + + file::FilePickOptions options; + + file::FilePickOptions::Filter filter; + filter.description = "QOLMod Label"; + filter.files = { "*.qollbl" }; + + options.filters.push_back(filter); + + file::pick(file::PickMode::SaveFile, options).listen([this, dump](Result* path) + { + if (path->isOk()) + { + auto filePath = path->unwrapOr(Mod::get()->getConfigDir()); + + if (!filePath.has_extension()) + filePath += ".qollbl"; + + auto res = file::writeString(filePath, dump); + + if (res.isOk()) + FLAlertLayer::create("Success!", "Success exporting file!", "OK")->show(); + else + FLAlertLayer::create("Failure!", fmt::format("Failed exporting file!\n{}", res.unwrapErr()), "OK")->show(); + } + }); } \ No newline at end of file diff --git a/src/Labels/LabelModule.hpp b/src/Labels/LabelModule.hpp index ab018fd0..44232bf8 100644 --- a/src/Labels/LabelModule.hpp +++ b/src/Labels/LabelModule.hpp @@ -42,4 +42,6 @@ class LabelModule : public Module matjson::Value saveToObject(); static LabelModule* createFromObject(matjson::Value obj); + + void exportToFile(); }; \ No newline at end of file diff --git a/src/Layers/EditLabelPopup.cpp b/src/Layers/EditLabelPopup.cpp index 5653591b..b70b20dd 100644 --- a/src/Layers/EditLabelPopup.cpp +++ b/src/Layers/EditLabelPopup.cpp @@ -233,6 +233,12 @@ void EditLabelPopup::customSetup() colourToggleMenu->addChild(noclipToggle); colourToggleMenu->addChild(noclipLabel); + // ButtonSprite * create(const char *caption, int width, bool absolute, const char *font, const char *texture, float height, float scale) + auto exportBtn = CCMenuItemSpriteExtra::create(ButtonSprite::create("Export To File", 100, false, "bigFont.fnt", "GJ_button_05.png", 30, 1.0f), this, menu_selector(EditLabelPopup::onExportToFile)); + exportBtn->setPosition(ccp((colourBG->getContentWidth() - 15 * 2) / 2, -120)); + exportBtn->getNormalImage()->setScale(0.85f); + colourToggleMenu->addChild(exportBtn); + colourBG->addChildAtPosition(colourToggleMenu, Anchor::TopLeft, ccp(15, -35)); auto infoMenu = CCMenu::create(); @@ -461,6 +467,11 @@ void EditLabelPopup::onPage(CCObject* sender) updatePage(); } +void EditLabelPopup::onExportToFile(CCObject* sender) +{ + module->exportToFile(); +} + void EditLabelPopup::onClose(CCObject* sender) { if (AndroidUI::get()) diff --git a/src/Layers/EditLabelPopup.hpp b/src/Layers/EditLabelPopup.hpp index a4d3a005..3e325eaf 100644 --- a/src/Layers/EditLabelPopup.hpp +++ b/src/Layers/EditLabelPopup.hpp @@ -30,6 +30,7 @@ class EditLabelPopup : public SillyBaseLayer void onClose(CCObject* sender); void onAddEvent(CCObject* sender); void onFormatInfo(CCObject* sender); + void onExportToFile(CCObject* sender); void updateList(); diff --git a/src/Layers/IconOptionsLayer.cpp b/src/Layers/IconOptionsLayer.cpp index 8db7f361..296d078b 100644 --- a/src/Layers/IconOptionsLayer.cpp +++ b/src/Layers/IconOptionsLayer.cpp @@ -1,5 +1,7 @@ #include "IconOptionsLayer.h" +#include "../Client/Windows/IconEffects.hpp" + void IconOptionsLayer::customSetup() { startFade = Mod::get()->getSavedValue(fmt::format("fadeColour1{}", icon), {0, 0, 0}); @@ -67,7 +69,10 @@ void IconOptionsLayer::onClose(CCObject* sender) { Mod::get()->setSavedValue(fmt::format("fadeColour1{}", icon), startFade); Mod::get()->setSavedValue(fmt::format("fadeColour2{}", icon), endFade); - Mod::get()->setSavedValue(fmt::format("icon-effect-speed_{}", icon), numFromString(input->getString()).unwrapOr(1.0f)); + + auto value = numFromString(input->getString()).unwrapOr(1.0f); + Mod::get()->setSavedValue(fmt::format("icon-effect-speed_{}", icon), value); + EffectUI::speeds[icon] = value; SillyBaseLayer::onClose(sender); } diff --git a/src/about.md b/src/about.md deleted file mode 100644 index 35f61289..00000000 --- a/src/about.md +++ /dev/null @@ -1,12 +0,0 @@ -# Join the [Discord Server](https://discord.gg/DfQSTEnQKK) - -QOLMod is The **Best** Free Mod Menu, It has a user friendly interface with over 70 features to help improve your Geometry Dash experience such as **Startpos Switcher**, **Show Hitboxes**, **Speedhack**, **Solid Wave Trail** and **much** more. - -# How to use. -On Windows / Mac: -- Press **Tab** or **Insert** on your keyboard -- The keybinds for opening the mod menu can be changed in the Mod Settings -- You can assign keybinds to mods in the **Config** category in the menu - -On Android: -- Press the button on your screen to open the mod menu. \ No newline at end of file