diff --git a/mod.json b/mod.json index e9ab706..589a612 100644 --- a/mod.json +++ b/mod.json @@ -17,6 +17,11 @@ "url": "https://github.com/geode-sdk/DevTools/issues", "info": "If you encounter an issue using DevTools, please report it to the GitHub issues page." }, + "resources": { + "sprites": [ + "resources/*.png" + ] + }, "settings": { "should-use-gd-window": { "type": "bool", diff --git a/resources/devtools.png b/resources/devtools.png new file mode 100644 index 0000000..741f0fe Binary files /dev/null and b/resources/devtools.png differ diff --git a/src/DevTools.cpp b/src/DevTools.cpp index 1434ca5..f40f1b1 100644 --- a/src/DevTools.cpp +++ b/src/DevTools.cpp @@ -26,7 +26,15 @@ struct matjson::Serialize { .showMemoryViewer = value["show_memory_viewer"].asBool().unwrapOr(std::move(defaults.showMemoryViewer)), .showModGraph = value["show_mod_graph"].asBool().unwrapOr(std::move(defaults.showModGraph)), .theme = value["theme"].asString().unwrapOr(std::move(defaults.theme)), - .themeColor = value["theme_color"].as().isOk() ? value["theme_color"].as().unwrap() : std::move(defaults.themeColor) + .themeColor = value["theme_color"].as().isOk() ? value["theme_color"].as().unwrap() : std::move(defaults.themeColor), + .buttonScale = value["button_scale"].as().unwrapOr(std::move(defaults.buttonScale)), + .buttonOpacity = value["button_opacity"].as().unwrapOr(std::move(defaults.buttonOpacity)), + .buttonInGameplay = value["button_gameplay"].asBool().unwrapOr(std::move(defaults.buttonInGameplay)), + .buttonInEditor = value["button_editor"].asBool().unwrapOr(std::move(defaults.buttonInEditor)), + .buttonPos = CCPoint{ + value["button_x"].as().unwrapOr(std::move(defaults.buttonPos.x)), + value["button_y"].as().unwrapOr(std::move(defaults.buttonPos.y)) + }, }); } @@ -43,6 +51,12 @@ struct matjson::Serialize { { "show_mod_graph", settings.showModGraph }, { "theme", settings.theme }, { "theme_color", settings.themeColor }, + { "button_scale", settings.buttonScale }, + { "button_opacity", settings.buttonOpacity }, + { "button_gameplay", settings.buttonInGameplay }, + { "button_editor", settings.buttonInEditor }, + { "button_x", settings.buttonPos.x }, + { "button_y", settings.buttonPos.y }, }); } }; @@ -54,7 +68,9 @@ DevTools* DevTools::get() { return inst; } -void DevTools::loadSettings() { m_settings = Mod::get()->getSavedValue("settings"); } +void DevTools::loadSettings() { + m_settings = Mod::get()->getSavedValue("settings"); +} void DevTools::saveSettings() { Mod::get()->setSavedValue("settings", m_settings); } Settings DevTools::getSettings() { return m_settings; } diff --git a/src/DevTools.hpp b/src/DevTools.hpp index e9da380..209324b 100644 --- a/src/DevTools.hpp +++ b/src/DevTools.hpp @@ -29,6 +29,11 @@ struct Settings { bool showModGraph = false; std::string theme = DARK_THEME; ccColor4B themeColor = {2, 119, 189, 255}; + float buttonScale = 1.f; + int buttonOpacity = 255; + bool buttonInGameplay = false; + bool buttonInEditor = false; + CCPoint buttonPos = {0, 0}; }; class DevTools { @@ -84,6 +89,7 @@ class DevTools { void loadSettings(); void saveSettings(); Settings getSettings(); + bool shouldUseGDWindow() const; bool shouldPopGame() const; diff --git a/src/main.cpp b/src/main.cpp index 9e2cd45..538446e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,4 +1,3 @@ - #include "platform/platform.hpp" #include #include @@ -30,17 +29,6 @@ class $modify(CCKeyboardDispatcher) { } }; -#ifdef GEODE_IS_MOBILE -// lol -#include -class $modify(MenuLayer) { - void onMoreGames(CCObject*) { - DevTools::get()->toggle(); - } -}; - -#endif - class $modify(CCDirector) { void willSwitchToScene(CCScene* scene) { CCDirector::willSwitchToScene(scene); @@ -112,3 +100,16 @@ class $modify(CCEGLView) { CCEGLView::swapBuffers(); } }; + + +// For the one eclipse shortcut +struct ToggleDevToolsEvent : geode::Event { + ToggleDevToolsEvent() {} +}; + +$on_mod(Loaded) { + new EventListener>(+[](ToggleDevToolsEvent* e) { + DevTools::get()->toggle(); + return ListenerResult::Stop; + }); +} \ No newline at end of file diff --git a/src/nodes/DragButton.cpp b/src/nodes/DragButton.cpp new file mode 100644 index 0000000..14b08f1 --- /dev/null +++ b/src/nodes/DragButton.cpp @@ -0,0 +1,174 @@ +#include "../DevTools.hpp" +#include "DragButton.hpp" + +DragButton *DragButton::m_instance = nullptr; + +bool DragButton::init() { + if (!CCMenu::init()) + return false; + + auto winSize = CCDirector::get()->getWinSize(); + + m_sprite = CircleButtonSprite::createWithSprite("devtools.png"_spr, 1, + CircleBaseColor::Green, CircleBaseSize::MediumAlt); + m_sprite->setScale(.8f); + m_sprite->setID("sprite"); + addChild(m_sprite); + setContentSize(m_sprite->getScaledContentSize()); + m_sprite->setPosition(getContentSize() / 2); + + CCScene::get()->addChild(this); + SceneManager::get()->keepAcrossScenes(this); + scheduleUpdate(); + + setZOrder(70000); + + auto settings = DevTools::get()->getSettings(); + setOpacity(settings.buttonOpacity); + setScale(settings.buttonScale); + settings.buttonPos.x = std::clamp(settings.buttonPos.x, -getContentWidth() / 2, winSize.width - getContentWidth() / 2); + settings.buttonPos.y = std::clamp(settings.buttonPos.y, -getContentHeight() / 2, winSize.height - getContentHeight() / 2); + setPosition(settings.buttonPos); + + setID("drag-button"_spr); + + return true; +}; + +DragButton *DragButton::get() { + if (m_instance) + return m_instance; + m_instance = new DragButton(); + if (m_instance && m_instance->init()) { + m_instance->autorelease(); + return m_instance; + } else { + delete m_instance; + return nullptr; + } +} + +void DragButton::registerWithTouchDispatcher() { + CCTouchDispatcher::get()->addTargetedDelegate(this, -512, true); +} + +bool DragButton::ccTouchBegan(CCTouch *touch, CCEvent *evt) { + if (!m_handleTouch || !m_bVisible) + return false; + if (getScaledContentSize().width / 2 < + ccpDistance(m_sprite->getPosition(), convertToNodeSpace(touch->getLocation()))) { + return false; + } + + m_diff = getPosition() - touch->getLocation(); + m_startPos = touch->getLocation(); + + m_moving = false; + + m_sprite->stopAllActions(); + + // For some reason I could not get a recreation of CCEaseSineOut working on ios. + #ifdef GEODE_IS_IOS + m_sprite->runAction(CCEaseOut::create(CCScaleTo::create(0.3f, .8 * m_scale * m_multiplier), 1.6f)); + #else + m_sprite->runAction(CCEaseSineOut::create(CCScaleTo::create(0.3f, .8 * m_scale * m_multiplier))); + #endif + return true; +} + +void DragButton::ccTouchCancelled(CCTouch *touch, CCEvent *event) { + ccTouchEnded(touch, event); +} + +void DragButton::ccTouchEnded(CCTouch *touch, CCEvent *evt) { + m_sprite->stopAllActions(); + + // For some reason I could not get a recreation of CCEaseSineOut working on ios. + #ifdef GEODE_IS_IOS + m_sprite->runAction(CCEaseOut::create(CCScaleTo::create(0.3f, .8 * m_scale), 1.6f)); + #else + m_sprite->runAction(CCEaseSineOut::create(CCScaleTo::create(0.3f, .8 * m_scale))); + #endif + if (m_moving) { + DevTools::get()->getSettings().buttonPos = getPosition(); + return; + } + activate(); +} + +void DragButton::ccTouchMoved(CCTouch *touch, CCEvent *evt) { + if (!m_moving) + if (ccpDistance(m_startPos, touch->getLocation()) > 3) + m_moving = true; + if (m_moving) { + auto pos = touch->getLocation() + m_diff; + pos.x = std::clamp(pos.x, -getContentWidth() / 2, CCDirector::get()->getWinSize().width - getContentWidth() / 2); + pos.y = std::clamp(pos.y, -getContentHeight() / 2, CCDirector::get()->getWinSize().height - getContentHeight() / 2); + setPosition(pos); + } +} + +void DragButton::update(float delta) { + static auto devtools = DevTools::get(); + bool shouldRender = true; + if (auto pl = PlayLayer::get(); pl && !pl->m_isPaused) { + shouldRender = devtools->getSettings().buttonInGameplay; + } else if(auto el = LevelEditorLayer::get(); el && !el->getChildByType(0)) { + if (devtools->getSettings().buttonInEditor) { + shouldRender = el->m_playbackMode != PlaybackMode::Playing || devtools->getSettings().buttonInGameplay; + } else { + shouldRender = false; + } + } + setVisible(shouldRender && m_render); +} + +bool DragButton::isRendered() { + return m_render; +} + +void DragButton::setRendered(bool render) { + m_render = render; +} + +bool DragButton::isHandlingTouch() { + return m_render && m_handleTouch; +} + +void DragButton::setHandlingTouch(bool handle) { m_handleTouch = handle; } + +void DragButton::activate() { + DevTools::get()->toggle(); +} + +// Only make it show if on mobile +#ifdef GEODE_IS_MOBILE +#include + +class $modify(CCScene) { + int getHighestChildZ() { + int btnZ; + auto btn = DragButton::get(); + if (btn) { + btnZ = btn->getZOrder(); + btn->setZOrder(-1); + } + auto highest = CCScene::getHighestChildZ(); + if (btn) { + btn->setZOrder(btnZ); + } + return highest; + } +}; + +#include + +class $modify(MenuLayer) { + bool init() { + if (!MenuLayer::init()) return false; + + DragButton::get(); + return true; + } +}; +#endif diff --git a/src/nodes/DragButton.hpp b/src/nodes/DragButton.hpp new file mode 100644 index 0000000..0315348 --- /dev/null +++ b/src/nodes/DragButton.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include + +using namespace geode::prelude; + +class DragButton : public cocos2d::CCMenu { +protected: + static DragButton *m_instance; + + bool m_handleTouch = true; + bool m_render = true; + bool m_moving = false; + + cocos2d::CCPoint m_startPos; + cocos2d::CCPoint m_diff; + cocos2d::CCSprite *m_sprite; + + float m_scale = 1.0f; + float m_multiplier = 0.8f; + + bool init() override; + void update(float delta) override; + bool ccTouchBegan(cocos2d::CCTouch *touch, cocos2d::CCEvent *event) override; + void ccTouchEnded(cocos2d::CCTouch *touch, cocos2d::CCEvent *event) override; + void ccTouchMoved(cocos2d::CCTouch *touch, cocos2d::CCEvent *event) override; + void ccTouchCancelled(cocos2d::CCTouch *touch, cocos2d::CCEvent *event) override; + void registerWithTouchDispatcher() override; +public: + static DragButton *get(); + void activate(); + bool isRendered(); + void setRendered(bool render); + bool isHandlingTouch(); + void setHandlingTouch(bool handle); +}; \ No newline at end of file diff --git a/src/pages/Settings.cpp b/src/pages/Settings.cpp index 20e852f..14a9b71 100644 --- a/src/pages/Settings.cpp +++ b/src/pages/Settings.cpp @@ -7,6 +7,7 @@ #include #include #include +#include "../nodes/DragButton.hpp" using namespace geode::prelude; @@ -230,6 +231,26 @@ void DevTools::drawSettings() { if (ImGui::Button("Reset Layout")) { m_shouldRelayout = true; } + + #ifdef GEODE_IS_MOBILE + auto button = DragButton::get(); + + ImGui::Separator(); + + ImGui::Text("Draggable Button"); + + if (ImGui::DragFloat("Scale", &m_settings.buttonScale, 0.05f, 0.5f, 1.f)) { + button->setScale(m_settings.buttonScale); + } + + if (ImGui::DragInt("Opacity", &m_settings.buttonOpacity, 1, 0, 255)) { + button->setOpacity(m_settings.buttonOpacity); + } + + ImGui::Checkbox("Visible in Game", &m_settings.buttonInGameplay); + ImGui::Checkbox("Visible in Editor", &m_settings.buttonInEditor); + + #endif } // TODO: this hook also isnt gd *