diff --git a/CMakeLists.txt b/CMakeLists.txt index 2b11c80a..e2808f04 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,6 +11,7 @@ file(GLOB SOURCES src/features/MoreTabs/*.cpp src/features/VisibilityToggle/*.cpp src/features/BetterScaling/*.cpp + src/features/StartPosSwitcher/*.cpp src/features/*.cpp src/other/*.cpp src/*.cpp diff --git a/api/include/MoreTabs.hpp b/api/include/MoreTabs.hpp index a14aff1a..6c436665 100644 --- a/api/include/MoreTabs.hpp +++ b/api/include/MoreTabs.hpp @@ -4,6 +4,12 @@ #include #include #include +#include +#include +#include +#include +#include +#include using namespace geode::prelude; diff --git a/api/src/MoreTabs.cpp b/api/src/MoreTabs.cpp index 36d5155b..2ab5aae6 100644 --- a/api/src/MoreTabs.cpp +++ b/api/src/MoreTabs.cpp @@ -1,10 +1,4 @@ #include -#include -#include -#include -#include -#include -#include using namespace keybinds; using namespace editor_api; diff --git a/mod.json b/mod.json index a1a85297..7a6a5cc4 100644 --- a/mod.json +++ b/mod.json @@ -67,7 +67,7 @@ "type": "bool", "default": true, "name": "Smart StartPos", - "description": "When enabled, all StartPoses in the editor will be automatically set for you" + "description": "When enabled, all StartPoses in the editor will be automatically set for you. Does not set gravity for ball and spider gamemodes." }, "playtest-lag-fix": { "type": "bool", diff --git a/src/Features/StartPosSwitcher/StartPosManager.cpp b/src/Features/StartPosSwitcher/StartPosManager.cpp new file mode 100644 index 00000000..975a664b --- /dev/null +++ b/src/Features/StartPosSwitcher/StartPosManager.cpp @@ -0,0 +1,193 @@ +#include "StartPosManager.hpp" + +StartPosManager* StartPosManager::get() { + if (!s_instance) { + auto instance = new StartPosManager; + if (instance) { + s_instance = instance; + return instance; + } + + CC_SAFE_DELETE(instance); + return instance; + } + return s_instance; +} + +void StartPosManager::setStartPositions(CCArray* objects) { + m_positions.clear(); + m_positions.push_back(CCPointZero); + if (!objects) { + return; + } + for (auto object : CCArrayExt(objects)) { + if (object->m_objectID == 31) { + auto skip = false; + for (auto position : m_positions) { + if (position == object->getPosition()) { + skip = true; + break; + } + } + if (skip) { + continue; + } + m_positions.push_back(object->getPosition()); + auto editor = LevelEditorLayer::get(); + if (m_active != CCPointZero && editor && !editor->m_editorInitialising) { + if (object->getPosition() == editor->m_editorUI->getGridSnappedPos(m_active)) { + m_active = object->getPosition(); + } + } + } + } + + this->sort(); + + if (m_positions.size() == 1) { + m_active = CCPointZero; + return; + } + if (m_initialized) { + return; + } + + m_active = m_positions.at(m_positions.size() - 1); + m_initialized = true; +} + +void StartPosManager::sort() { + std::sort(m_positions.begin(), m_positions.end(), [](CCPoint first, CCPoint second) { + return first.x < second.x; + }); +} + +void StartPosManager::setActive(CCPoint const& pos) { + bool set = false; + for (auto startPos : m_positions) { + if (startPos == pos) { + m_active = pos; + set = true; + break; + } + } + + if (!set) { + m_active = m_positions.at(m_positions.size() - 1); + } +} + +void StartPosManager::addStartPos(CCPoint const& position) { + for (auto startPosition : m_positions) { + if (startPosition == position) { + return; + } + } + m_positions.push_back(position); + this->sort(); +} + +void StartPosManager::replaceStartPos(CCPoint const& before, CCPoint const& after) { + if (!LevelEditorLayer::get() || LevelEditorLayer::get()->m_editorInitialising) { + return; + } + + for (auto& position : m_positions) { + if (position == before) { + position = after; + break; + } + } + if (m_active == before) { + m_active = after; + } + + this->sort(); +} + +StartPosObject* StartPosManager::getStartPosFromPoint(CCPoint const& point) { + if (LevelEditorLayer::get()) { + for (auto object : CCArrayExt(LevelEditorLayer::get()->m_objects)) { + if (object->m_objectID == 31) { + if (object->getPosition() == point) { + return static_cast(object); + } + + if (!LevelEditorLayer::get()->m_editorInitialising && object->getPosition() == LevelEditorLayer::get()->m_editorUI->getGridSnappedPos(point)) { + this->replaceStartPos(point, LevelEditorLayer::get()->m_editorUI->getGridSnappedPos(point)); + return static_cast(object); + } + } + } + + return nullptr; + } + + if (PlayLayer::get()) { + for (auto object : CCArrayExt(PlayLayer::get()->m_objects)) { + if ( + object->m_objectID == 31 && + object->getPosition() == point + ) { + return static_cast(object); + } + } + } + return nullptr; +} + +void StartPosManager::next() { + for (auto pos : m_positions) { + if (pos.x > m_active.x) { + m_active = pos; + return; + } + } +} + +void StartPosManager::previous() { + auto found = CCPointZero; + for (auto pos : m_positions) { + if (pos.x < m_active.x) { + found = pos; + } + } + + m_active = found; +} + +void StartPosManager::clear() { + m_active = CCPointZero; + m_positions.clear(); + m_initialized = false; +} + +bool StartPosManager::isDefault() { + if (m_positions.size() == 0) { + return true; + } + return m_active == m_positions.at(m_positions.size() - 1); +} + +bool StartPosManager::isLevelStart() { + if (m_positions.size() == 0) { + return false; + } + return m_active == m_positions.at(0); +} + +CCPoint StartPosManager::getActive() { + return m_active; +} + +std::vector StartPosManager::getPositions() { + return m_positions; +} + +void StartPosManager::setDefault() { + m_active = m_positions.at(m_positions.size() - 1); +} + +void StartPosManager::setFirst() { + m_active = m_positions.at(0); +} \ No newline at end of file diff --git a/src/Features/StartPosSwitcher/StartPosManager.hpp b/src/Features/StartPosSwitcher/StartPosManager.hpp new file mode 100644 index 00000000..62418c37 --- /dev/null +++ b/src/Features/StartPosSwitcher/StartPosManager.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include +#include +#include + +using namespace geode::prelude; + +class StartPosManager : public CCObject { +protected: + std::vector m_positions = {}; + CCPoint m_active = CCPointZero; + bool m_initialized = false; + + inline static StartPosManager* s_instance; + void sort(); +public: + static StartPosManager* get(); + void setStartPositions(CCArray* objects); + void setActive(CCPoint const& pos); + void addStartPos(CCPoint const& pos); + void replaceStartPos(CCPoint const& before, CCPoint const& after); + bool isDefault(); + bool isLevelStart(); + StartPosObject* getStartPosFromPoint(CCPoint const& pos); + CCPoint getActive(); + std::vector getPositions(); + void next(); + void previous(); + void clear(); + void setDefault(); + void setFirst(); +}; \ No newline at end of file diff --git a/src/features/LDMObjectCount.cpp b/src/features/LDMObjectCount.cpp index 468d887e..098170f7 100644 --- a/src/features/LDMObjectCount.cpp +++ b/src/features/LDMObjectCount.cpp @@ -15,11 +15,20 @@ class $modify(LDMCountEditorPauseLayer, EditorPauseLayer) { auto menu = static_cast(this->getChildByID("info-menu")); auto objectCountLabel = static_cast(menu->getChildByID("object-count-label")); + auto objectCount = this->countValidObjects(); + std::string objectCountText = objectCountLabel->getString(); std::stringstream ss; ss << std::string(objectCountLabel->getString()); - int ldmCount = this->countLDMObjects(); - float percentage = static_cast(ldmCount) / this->countValidObjects() * 100; + int ldmCount; + float percentage; + if (objectCount == 0) { + ldmCount = 0; + percentage = 0.f; + } else { + ldmCount = this->countLDMObjects(); + percentage = static_cast(ldmCount) / objectCount * 100; + } ss << " | " << ldmCount << " LDM (" << std::fixed << std::setprecision(2) << percentage << "%)"; objectCountLabel->setString(ss.str().c_str()); diff --git a/src/features/SmartStartPos.cpp b/src/features/SmartStartPos.cpp index bfbccec3..9d80d6e5 100644 --- a/src/features/SmartStartPos.cpp +++ b/src/features/SmartStartPos.cpp @@ -18,22 +18,7 @@ class $modify(LevelEditorLayer) { } void setupStartPos(StartPosObject* startPos) { - for (size_t i = 0; i < gravityPortals.size(); i++) - { - if (gravityPortals[i]->getPositionX() - 10 > startPos->getPositionX()) - break; - if (gravityPortals[i]->getPositionX() - 10 < startPos->getPositionX()) - startPos->m_levelSettings->m_isFlipped = gravityPortals[i]->m_objectID == 11; - } - - startPos->m_levelSettings->m_startDual = LevelEditorLayer::get()->m_levelSettings->m_startDual; - for (size_t i = 0; i < dualPortals.size(); i++) - { - if (dualPortals[i]->getPositionX() - 10 > startPos->getPositionX()) - break; - if (dualPortals[i]->getPositionX() - 10 < startPos->getPositionX()) - startPos->m_levelSettings->m_startDual = dualPortals[i]->m_objectID == 286; - } + bool isBallOrSpider = false; startPos->m_levelSettings->m_startMode = LevelEditorLayer::get()->m_levelSettings->m_startMode; for (size_t i = 0; i < gamemodePortals.size(); i++) { @@ -51,6 +36,7 @@ class $modify(LevelEditorLayer) { break; case 47: startPos->m_levelSettings->m_startMode = (int)IconType::Ball; + isBallOrSpider = true; break; case 111: startPos->m_levelSettings->m_startMode = (int)IconType::Ufo; @@ -63,10 +49,29 @@ class $modify(LevelEditorLayer) { break; case 1331: startPos->m_levelSettings->m_startMode = (int)IconType::Spider; + isBallOrSpider = true; break; } } } + if (!isBallOrSpider) { + for (size_t i = 0; i < gravityPortals.size(); i++) + { + if (gravityPortals[i]->getPositionX() - 10 > startPos->getPositionX()) + break; + if (gravityPortals[i]->getPositionX() - 10 < startPos->getPositionX()) + startPos->m_levelSettings->m_isFlipped = gravityPortals[i]->m_objectID == 11; + } + } + + startPos->m_levelSettings->m_startDual = LevelEditorLayer::get()->m_levelSettings->m_startDual; + for (size_t i = 0; i < dualPortals.size(); i++) + { + if (dualPortals[i]->getPositionX() - 10 > startPos->getPositionX()) + break; + if (dualPortals[i]->getPositionX() - 10 < startPos->getPositionX()) + startPos->m_levelSettings->m_startDual = dualPortals[i]->m_objectID == 286; + } startPos->m_levelSettings->m_startMini = LevelEditorLayer::get()->m_levelSettings->m_startMini; for (size_t i = 0; i < miniPortals.size(); i++) @@ -215,7 +220,8 @@ class $modify(LevelEditorLayer) { speedChanges.push_back(g); break; } - - SSPsetup(); + if (LevelEditorLayer::get() && !LevelEditorLayer::get()->m_editorInitialising && g->m_objectID == 31) { + this->setupStartPos(static_cast(g)); + } } }; \ No newline at end of file diff --git a/src/features/StartPosSwitcher.cpp b/src/features/StartPosSwitcher.cpp deleted file mode 100644 index 0bc3cf7c..00000000 --- a/src/features/StartPosSwitcher.cpp +++ /dev/null @@ -1,412 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#undef min -#undef max - -using namespace geode::prelude; -using namespace editor_api; - -struct FromLevelStart {}; -struct DefaultBehaviour {}; -using FromPoint = CCPoint; -using FromObj = StartPosObject*; -using StartPosKind = std::variant< - FromLevelStart, - DefaultBehaviour, - FromPoint, - FromObj ->; - -class $modify(StartPosLevel, GJGameLevel) { - StartPosKind m_startPos; - - StartPosLevel() : m_startPos(DefaultBehaviour()) {} - - bool match(CCPoint const& pos) { - if (std::holds_alternative(m_fields->m_startPos)) { - return std::get(m_fields->m_startPos) == pos; - } - return false; - } -}; - -class PlaytestHerePopup : public CCNode { -protected: - Ref m_startPos; - LevelEditorLayer* m_editor; - static Ref s_popup; - - bool init(LevelEditorLayer* lel, StartPosObject* startPos) { - if (!CCNode::init()) - return false; - - m_editor = lel; - m_startPos = startPos; - - this->setZOrder(0xB00B5); - - auto bg = CCScale9Sprite::create("square02_001.png", { 0, 0, 80, 80 }); - bg->setScale(.5f); - bg->setOpacity(80); - bg->setContentSize({ 120, 60 }); - this->addChild(bg); - - auto menu = CCMenu::create(); - menu->setPosition(0, 0); - - auto playTestBtnSpr = CCSprite::createWithSpriteFrameName("GJ_playEditorBtn_001.png"); - playTestBtnSpr->setScale(.615f); - - auto playTestBtn = CCMenuItemSpriteExtra::create( - playTestBtnSpr, this, menu_selector(PlaytestHerePopup::onPlaytest) - ); - playTestBtn->setPosition(-15.f, .0f); - menu->addChild(playTestBtn); - - auto playInGameBtnSpr = CCSprite::createWithSpriteFrameName("GJ_playBtn2_001.png"); - playInGameBtnSpr->setScale(.3f); - - auto playInGameBtn = CCMenuItemSpriteExtra::create( - playInGameBtnSpr, this, menu_selector(PlaytestHerePopup::onPlayInGame) - ); - playInGameBtn->setPosition(15.f, .0f); - menu->addChild(playInGameBtn); - - this->addChild(menu); - - CCDirector::sharedDirector()->getTouchDispatcher()->incrementForcePrio(2); - menu->registerWithTouchDispatcher(); - - s_popup = this; - - return true; - } - - static PlaytestHerePopup* create( - LevelEditorLayer* lel, StartPosObject* startPos - ) { - auto ret = new PlaytestHerePopup; - if (ret && ret->init(lel, startPos)) { - ret->autorelease(); - return ret; - } - CC_SAFE_DELETE(ret); - return nullptr; - } - - // has to be defined outside because it used StartPosSwitchLayer - void onPlaytest(CCObject*); - void onPlayInGame(CCObject*) { - this->select(); - m_editor->m_editorUI->onPause(nullptr); - EditorPauseLayer::get()->setVisible(false); - EditorPauseLayer::get()->onSaveAndPlay(nullptr); - } - -public: - void select() { - static_cast( - m_editor->m_level - )->m_fields->m_startPos = m_startPos->getPosition(); - } - - static void move() { - if (s_popup) { - s_popup->setPosition( - s_popup->m_startPos->getPositionX(), - s_popup->m_startPos->getPositionY() + 35.f - ); - } - } - - static void show(LevelEditorLayer* lel, StartPosObject* startPos) { - PlaytestHerePopup::hide(); - PlaytestHerePopup::create(lel, startPos); - PlaytestHerePopup::move(); - lel->m_objectLayer->addChild(s_popup); - } - - static void hide() { - if (s_popup) { - s_popup->removeFromParent(); - s_popup = nullptr; - } - } -}; -Ref PlaytestHerePopup::s_popup = nullptr; - -class $modify(PlayLayer) { - void addObject(GameObject* obj) { - auto oldStartPos = m_startPos; - PlayLayer::addObject(obj); - if (oldStartPos) { - m_startPos = oldStartPos; - m_playerStartPosition = oldStartPos->getPosition(); - } - if (obj->m_objectID == 31) { - if (static_cast(m_level)->match(obj->getPosition())) { - m_startPos = static_cast(obj); - m_playerStartPosition = obj->getPosition(); - } - if (std::holds_alternative(static_cast( - m_level - )->m_fields->m_startPos)) { - m_startPos = nullptr; - m_playerStartPosition = CCPoint { 0, 105.f }; - } - } - } -}; - -class $modify(StartPosSwitchLayer, LevelEditorLayer) { - StartPosKind m_selectedStartPos; - - StartPosSwitchLayer() : m_selectedStartPos(DefaultBehaviour()) { - PlaytestHerePopup::hide(); - } - - void addSpecial(GameObject* obj) { - LevelEditorLayer::addSpecial(obj); - if (obj->m_objectID == 31) { - if (static_cast(m_level)->match(obj->getPosition())) { - m_fields->m_selectedStartPos = static_cast(obj); - } - } - } - - // void destructor() { - // PlaytestHerePopup::hide(); - // LevelEditorLayer::~LevelEditorLayer(); - // } - - void handleAction(bool idk, CCArray* idk2) { - LevelEditorLayer::handleAction(idk, idk2); - PlaytestHerePopup::move(); - } - - void setupLevelStart(LevelSettingsObject* obj) { - // selected start position - if (std::holds_alternative(m_fields->m_selectedStartPos)) { - auto obj = std::get(m_fields->m_selectedStartPos); - this->setStartPosObject(obj); - auto startPos = obj->getPosition(); - m_player1->setStartPos(startPos); - m_player1->resetObject(); - m_player2->setStartPos(startPos); - m_player2->resetObject(); - LevelEditorLayer::setupLevelStart(obj->m_levelSettings); - } - // from level start - else if (std::holds_alternative(m_fields->m_selectedStartPos)) { - this->setStartPosObject(nullptr); - m_player1->setStartPos({ 0.f, 105.f }); - m_player1->resetObject(); - m_player2->setStartPos({ 0.f, 105.f }); - m_player2->resetObject(); - LevelEditorLayer::setupLevelStart(m_levelSettings); - } - // default behaviour - else { - LevelEditorLayer::setupLevelStart(obj); - } - } -}; - -$onEditorExit { - PlaytestHerePopup::hide(); -} - -void PlaytestHerePopup::onPlaytest(CCObject*) { - this->select(); - static_cast( - m_editor - )->m_fields->m_selectedStartPos = m_startPos; - m_editor->setStartPosObject(m_startPos); - EditorUI::get()->onPlaytest(EditorUI::get()->m_playtestBtn); -} - -class $modify(EditorPauseLayer) { - bool init(LevelEditorLayer* lel) { - if (!EditorPauseLayer::init(lel)) - return false; - - PlaytestHerePopup::hide(); - - return true; - } -}; - -class StartPosButtonBar : public CCMenu { -protected: - LevelEditorLayer* m_editor; - - bool init(LevelEditorLayer* lel) { - if (!CCMenu::init()) - return false; - - m_editor = lel; - - auto winSize = CCDirector::get()->getWinSize(); - - this->ignoreAnchorPointForPosition(false); - this->setPosition(winSize.width / 2, 45.f); - this->setContentSize({ 200.f, 60.f }); - - auto goToStartSpr = ButtonSprite::create( - CCSprite::createWithSpriteFrameName("start-from-start.png"_spr), - 0x32, true, 0x32, "GJ_button_01.png", .7f - ); - auto goToStartBtn = CCMenuItemSpriteExtra::create( - goToStartSpr, this, menu_selector(StartPosButtonBar::onGoToStart) - ); - goToStartBtn->setPosition( - m_obContentSize.width / 2 - 25.f, - m_obContentSize.height / 2 - ); - this->addChild(goToStartBtn); - - auto goToLastSpr = ButtonSprite::create( - CCSprite::createWithSpriteFrameName("last-start-pos.png"_spr), - 0x32, true, 0x32, "GJ_button_01.png", .75f - ); - auto goToLastBtn = CCMenuItemSpriteExtra::create( - goToLastSpr, this, menu_selector(StartPosButtonBar::onGoToLast) - ); - goToLastBtn->setPosition( - m_obContentSize.width / 2 + 25.f, - m_obContentSize.height / 2 - ); - this->addChild(goToLastBtn); - - auto infoSpr = CCSprite::createWithSpriteFrameName("GJ_infoIcon_001.png"); - auto infoBtn = CCMenuItemSpriteExtra::create( - infoSpr, this, menu_selector(StartPosButtonBar::onInfo) - ); - infoBtn->setPosition(m_obContentSize / 2 + ccp(60.f, 0.f)); - this->addChild(infoBtn); - - return true; - } - - void onInfo(CCObject*) { - FLAlertLayer::create( - "Start Pos Info", - "From Level Start: Start playing from the beginning of the " - "level, ignoring all start positions\n" - "From Last Start Pos: Start playing from the last start " - "position in the level", - "OK" - )->show(); - } - - void onGoToStart(CCObject*) { - static_cast( - m_editor - )->m_fields->m_selectedStartPos = FromLevelStart(); - static_cast( - m_editor->m_level - )->m_fields->m_startPos = FromLevelStart(); - } - - void onGoToLast(CCObject*) { - StartPosObject* last = nullptr; - for(auto* obj : cocos::CCArrayExt(m_editor->m_objects)) { - if (obj->m_objectID == 31) { - if (!last || last->getPositionX() < obj->getPositionX()) { - last = obj; - } - } - } - if (last) { - static_cast( - m_editor - )->m_fields->m_selectedStartPos = last; - static_cast( - m_editor->m_level - )->m_fields->m_startPos = last->getPosition(); - } else { - static_cast( - m_editor - )->m_fields->m_selectedStartPos = DefaultBehaviour(); - static_cast( - m_editor->m_level - )->m_fields->m_startPos = DefaultBehaviour(); - } - } - -public: - static StartPosButtonBar* create(LevelEditorLayer* lel) { - auto ret = new StartPosButtonBar; - if (ret && ret->init(lel)) { - ret->autorelease(); - return ret; - } - CC_SAFE_DELETE(ret); - return nullptr; - } -}; - -class $modify(MyEditorUI, EditorUI) { - bool init(LevelEditorLayer* lel) { - if (!EditorUI::init(lel)) - return false; - - MoreTabs::get(this)->addEditTab( - "start-pos-border.png"_spr, - StartPosButtonBar::create(m_editorLayer) - ); - - return true; - } - - void deleteObject(GameObject* obj, bool filter) { - EditorUI::deleteObject(obj, filter); - PlaytestHerePopup::hide(); - } - - void selectObjects(CCArray* objects, bool ignoreFilters) { - EditorUI::selectObjects(objects, ignoreFilters); - PlaytestHerePopup::hide(); - } - - void selectObject(GameObject* obj, bool filter) { - EditorUI::selectObject(obj, filter); - MoreTabs::get(this)->switchEditTab(1); - } - - void moveObject(GameObject* obj, CCPoint pos) { - EditorUI::moveObject(obj, pos); - PlaytestHerePopup::move(); - } - - void moveObjectCall(EditCommand cmd) { - EditorUI::moveObjectCall(cmd); - PlaytestHerePopup::move(); - } - - void deselectAll() { - EditorUI::deselectAll(); - PlaytestHerePopup::hide(); - } -}; - -class $modify(GameObject) { - void selectObject(ccColor3B color) { - GameObject::selectObject(color); - PlaytestHerePopup::hide(); - if (m_objectID == 31) { - PlaytestHerePopup::show( - LevelEditorLayer::get(), - static_cast(as(this)) - ); - } - } -}; diff --git a/src/features/StartPosSwitcher/PlaytestHerePopup.cpp b/src/features/StartPosSwitcher/PlaytestHerePopup.cpp new file mode 100644 index 00000000..fb8e9b00 --- /dev/null +++ b/src/features/StartPosSwitcher/PlaytestHerePopup.cpp @@ -0,0 +1,67 @@ +#include "PlaytestHerePopup.hpp" + +bool PlaytestHerePopup::init(LevelEditorLayer* lel, StartPosObject* startPos, std::function callback) { + if (!CCNode::init()) + return false; + + m_editor = lel; + m_startPos = startPos; + m_callback = callback; + + this->setZOrder(0xB00B5); + + auto bg = CCScale9Sprite::create("square02_001.png", { 0, 0, 80, 80 }); + bg->setScale(.5f); + bg->setOpacity(80); + bg->setContentSize({ 120, 60 }); + this->addChild(bg); + + auto menu = CCMenu::create(); + menu->setPosition(0, 0); + + auto playTestBtnSpr = CCSprite::createWithSpriteFrameName("GJ_playEditorBtn_001.png"); + playTestBtnSpr->setScale(.615f); + + auto playTestBtn = CCMenuItemSpriteExtra::create( + playTestBtnSpr, this, menu_selector(PlaytestHerePopup::onPlaytest) + ); + playTestBtn->setPosition(-15.f, .0f); + menu->addChild(playTestBtn); + + auto playInGameBtnSpr = CCSprite::createWithSpriteFrameName("GJ_playBtn2_001.png"); + playInGameBtnSpr->setScale(.3f); + + auto playInGameBtn = CCMenuItemSpriteExtra::create( + playInGameBtnSpr, this, menu_selector(PlaytestHerePopup::onPlayInGame) + ); + playInGameBtn->setPosition(15.f, .0f); + menu->addChild(playInGameBtn); + + this->addChild(menu); + + CCDirector::sharedDirector()->getTouchDispatcher()->incrementForcePrio(2); + menu->registerWithTouchDispatcher(); + + s_popup = this; + + return true; +} + +void PlaytestHerePopup::onPlaytest(CCObject*) { + this->select(); + m_callback(); + m_editor->setStartPosObject(m_startPos); + EditorUI::get()->onPlaytest(EditorUI::get()->m_playtestBtn); +} + +void PlaytestHerePopup::onPlayInGame(CCObject*) { + this->select(); + m_callback(); + m_editor->m_editorUI->onPause(nullptr); + EditorPauseLayer::get()->onSaveAndPlay(nullptr); + EditorPauseLayer::get()->setVisible(false); +} + +void PlaytestHerePopup::select() { + StartPosManager::get()->setActive(m_startPos->getPosition()); +} \ No newline at end of file diff --git a/src/features/StartPosSwitcher/PlaytestHerePopup.hpp b/src/features/StartPosSwitcher/PlaytestHerePopup.hpp new file mode 100644 index 00000000..e782a230 --- /dev/null +++ b/src/features/StartPosSwitcher/PlaytestHerePopup.hpp @@ -0,0 +1,57 @@ +#pragma once + +#include + +#include "StartPosManager.hpp" + +using namespace geode::prelude; + +class PlaytestHerePopup : public CCNode { +protected: + Ref m_startPos; + LevelEditorLayer* m_editor; + inline static PlaytestHerePopup* s_popup = nullptr; + std::function m_callback; + + bool init(LevelEditorLayer*, StartPosObject*, std::function); + + static PlaytestHerePopup* create( + LevelEditorLayer* lel, StartPosObject* startPos, std::function callback + ) { + auto ret = new PlaytestHerePopup; + if (ret && ret->init(lel, startPos, callback)) { + ret->autorelease(); + return ret; + } + CC_SAFE_DELETE(ret); + return nullptr; + } + + void onPlaytest(CCObject*); + void onPlayInGame(CCObject*); +public: + void select(); + + static void move() { + if (s_popup) { + s_popup->setPosition( + s_popup->m_startPos->getPositionX(), + s_popup->m_startPos->getPositionY() + 35.f + ); + } + } + + static void show(LevelEditorLayer* lel, StartPosObject* startPos, std::function callback) { + PlaytestHerePopup::hide(); + PlaytestHerePopup::create(lel, startPos, callback); + PlaytestHerePopup::move(); + lel->m_objectLayer->addChild(s_popup); + } + + static void hide() { + if (s_popup) { + s_popup->removeFromParent(); + s_popup = nullptr; + } + } +}; \ No newline at end of file diff --git a/src/features/StartPosSwitcher/StartPosButtonBar.cpp b/src/features/StartPosSwitcher/StartPosButtonBar.cpp new file mode 100644 index 00000000..3fbdda0b --- /dev/null +++ b/src/features/StartPosSwitcher/StartPosButtonBar.cpp @@ -0,0 +1,158 @@ +#include "StartPosButtonBar.hpp" + +bool StartPosButtonBar::init(LevelEditorLayer* lel) { + if (!CCMenu::init()) + return false; + + m_editor = lel; + + auto winSize = CCDirector::get()->getWinSize(); + + this->ignoreAnchorPointForPosition(false); + this->setPosition(winSize.width / 2, 45.f); + this->setContentSize({ 200.f, 60.f }); + + auto goToStartSpr = ButtonSprite::create( + CCSprite::createWithSpriteFrameName("start-from-start.png"_spr), + 0x32, true, 0x32, "GJ_button_01.png", .7f + ); + auto goToStartBtn = CCMenuItemSpriteExtra::create( + goToStartSpr, this, menu_selector(StartPosButtonBar::onGoToStart) + ); + goToStartBtn->setPosition( + m_obContentSize.width / 2 - 25.f, + m_obContentSize.height / 2 + 20.f + ); + this->addChild(goToStartBtn); + + auto counterLabel = CCLabelBMFont::create("", "bigFont.fnt"); + counterLabel->setScale(0.6f); + counterLabel->setPosition({ m_obContentSize.width / 2, m_obContentSize.height / 2 - 25.f }); + counterLabel->limitLabelWidth(60.0f, 0.6f, 0.1f); + counterLabel->setString("0 / 0"); + m_counterLabel = counterLabel; + this->addChild(counterLabel); + this->setStartPosCounters(); + + auto goToLastSpr = ButtonSprite::create( + CCSprite::createWithSpriteFrameName("last-start-pos.png"_spr), + 0x32, true, 0x32, "GJ_button_01.png", .75f + ); + auto goToLastBtn = CCMenuItemSpriteExtra::create( + goToLastSpr, this, menu_selector(StartPosButtonBar::onGoToLast) + ); + goToLastBtn->setPosition( + m_obContentSize.width / 2 + 25.f, + m_obContentSize.height / 2 + 20.f + ); + this->addChild(goToLastBtn); + + auto infoSpr = CCSprite::createWithSpriteFrameName("GJ_infoIcon_001.png"); + infoSpr->setScale(0.8f); + auto infoBtn = CCMenuItemSpriteExtra::create( + infoSpr, this, menu_selector(StartPosButtonBar::onInfo) + ); + infoBtn->setPosition(m_obContentSize / 2 + ccp(65.f, 20.f)); + this->addChild(infoBtn); + + auto arrowLeft = CCSprite::createWithSpriteFrameName("GJ_arrow_03_001.png"); + arrowLeft->setScale(0.7f); + auto leftbutton = CCMenuItemSpriteExtra::create( + arrowLeft, this, menu_selector(StartPosButtonBar::onPrevious) + ); + leftbutton->setPosition(m_obContentSize / 2 + ccp(-45.0f, -25.0f)); + this->addChild(leftbutton); + + auto arrowRight = CCSprite::createWithSpriteFrameName("GJ_arrow_03_001.png"); + arrowRight->setScale(0.7f); + arrowRight->setFlipX(true); + auto rightButton = CCMenuItemSpriteExtra::create( + arrowRight, this, menu_selector(StartPosButtonBar::onNext) + ); + rightButton->setPosition(m_obContentSize / 2 + ccp(45.0f, -25.0f)); + this->addChild(rightButton); + + return true; +} + +void StartPosButtonBar::onInfo(CCObject*) { + FLAlertLayer::create( + "Start Pos Info", + "From Level Start: Start playing from the beginning of the " + "level, ignoring all start positions\n" + "From Last Start Pos: Start playing from the last start " + "position in the level", + "OK" + )->show(); +} + +void StartPosButtonBar::onGoToStart(CCObject*) { + StartPosManager::get()->setFirst(); + this->setStartPosCounters(); +} + +void StartPosButtonBar::onGoToLast(CCObject*) { + StartPosManager::get()->setDefault(); + this->setStartPosCounters(); +} + +void StartPosButtonBar::setStartPosCounters() { + m_totalStartPositions = 0; + m_activeIndex = 0; + if (!m_editor || !m_editor->m_objects) { + return; + } + + m_totalStartPositions = StartPosManager::get()->getPositions().size() - 1; + auto active = StartPosManager::get()->getActive(); + + bool found = false; + + if (StartPosManager::get()->isDefault()) { + m_activeIndex = m_totalStartPositions; + found = true; + } + + if (StartPosManager::get()->isLevelStart() && !found) { + m_activeIndex = 0; + found = true; + } + + if (!found) { + int i = 0; + for (auto pos : StartPosManager::get()->getPositions()) { + if (pos == active) { + m_activeIndex = i; + } + i++; + } + } + + std::string str = std::to_string(m_activeIndex) + " / " + std::to_string(m_totalStartPositions); + + m_counterLabel->setString(str.c_str()); + m_counterLabel->limitLabelWidth(60.0f, 0.6f, 0.1f); +} + +void StartPosButtonBar::onNext(CCObject*) { + if (m_activeIndex == m_totalStartPositions) { + return; + } + + StartPosManager::get()->next(); + this->setStartPosCounters(); + editor_api::moveGameLayerTo(m_editor->m_editorUI, StartPosManager::get()->getStartPosFromPoint(StartPosManager::get()->getActive())); +} + +void StartPosButtonBar::onPrevious(CCObject*) { + if (m_activeIndex == 0) { + return; + } + + StartPosManager::get()->previous(); + this->setStartPosCounters(); + if (StartPosManager::get()->getActive() == CCPointZero) { + return; + } + editor_api::moveGameLayerTo(m_editor->m_editorUI, StartPosManager::get()->getStartPosFromPoint(StartPosManager::get()->getActive())); +} \ No newline at end of file diff --git a/src/features/StartPosSwitcher/StartPosButtonBar.hpp b/src/features/StartPosSwitcher/StartPosButtonBar.hpp new file mode 100644 index 00000000..c0b32596 --- /dev/null +++ b/src/features/StartPosSwitcher/StartPosButtonBar.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include +#include +#include +#include + +#include "StartPosManager.hpp" + +using namespace geode::prelude; + +class StartPosButtonBar : public CCMenu { +protected: + LevelEditorLayer* m_editor; + CCLabelBMFont* m_counterLabel = nullptr; + + int m_totalStartPositions = 0; + int m_activeIndex = 0; + + bool init(LevelEditorLayer*); +public: + void setStartPosCounters(); + void onInfo(CCObject*); + void onGoToStart(CCObject*); + void onGoToLast(CCObject*); + void onNext(CCObject*); + void onPrevious(CCObject*); + static StartPosButtonBar* create(LevelEditorLayer* lel) { + auto ret = new StartPosButtonBar; + if (ret && ret->init(lel)) { + ret->autorelease(); + return ret; + } + CC_SAFE_DELETE(ret); + return nullptr; + } +}; \ No newline at end of file diff --git a/src/features/StartPosSwitcher/StartPosSwitcher.cpp b/src/features/StartPosSwitcher/StartPosSwitcher.cpp new file mode 100644 index 00000000..09c17139 --- /dev/null +++ b/src/features/StartPosSwitcher/StartPosSwitcher.cpp @@ -0,0 +1,293 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#undef min +#undef max + +#include "PlaytestHerePopup.hpp" +#include "StartPosButtonBar.hpp" +#include "StartPosManager.hpp" + +using namespace geode::prelude; +using namespace editor_api; + +bool g_enteringLevel = false; + +class $modify(PlayLayer) { + StartPosObject* activeStartPos = nullptr; + bool fromLevelStart = false; + void addObject(GameObject* obj) { + g_enteringLevel = false; + PlayLayer::addObject(obj); + if (obj->m_objectID != 31) { + return; + } + if (StartPosManager::get()->isDefault() && !m_fields->activeStartPos) { + return; + } + if (StartPosManager::get()->isLevelStart() && !m_fields->activeStartPos) { + m_startPos = nullptr; + m_playerStartPosition = CCPointZero; + return; + } + + auto active = StartPosManager::get()->getActive(); + if (obj->getPosition() == StartPosManager::get()->getActive() && !m_fields->activeStartPos) { + m_fields->activeStartPos = static_cast(obj); + } + + if (m_fields->activeStartPos) { + m_startPos = m_fields->activeStartPos; + m_playerStartPosition = m_fields->activeStartPos->getPosition(); + } + } +}; + +class $modify(StartPosSwitchLayer, LevelEditorLayer) { + bool init(GJGameLevel* level) { + if (!LevelEditorLayer::init(level)) { + return false; + } + StartPosManager::get()->setStartPositions(m_objects); + auto bar = static_cast(m_editorUI->getChildByID("start-pos-button-bar"_spr)); + if (bar) { + bar->setStartPosCounters(); + } + + return true; + } + void addSpecial(GameObject* obj) { + LevelEditorLayer::addSpecial(obj); + if (!m_editorInitialising && obj->m_objectID == 31) { + StartPosManager::get()->addStartPos(obj->getPosition()); + auto bar = static_cast(m_editorUI->getChildByID("start-pos-button-bar"_spr)); + if (bar) { + bar->setStartPosCounters(); + } + } + } + + void removeSpecial(GameObject* obj) { + LevelEditorLayer::removeSpecial(obj); + if (obj->m_objectID != 31) { + return; + } + if (g_enteringLevel) { + return; + } + PlaytestHerePopup::hide(); + if (obj->m_objectID == 31) { + auto manager = StartPosManager::get(); + if (obj->getPosition() == manager->getActive()) { + manager->clear(); + } + manager->setStartPositions(m_objects); + auto buttonBar = static_cast(m_editorUI->getChildByID("start-pos-button-bar"_spr)); + buttonBar->setStartPosCounters(); + } + } + + void handleAction(bool idk, CCArray* idk2) { + LevelEditorLayer::handleAction(idk, idk2); + PlaytestHerePopup::move(); + } + + void setupLevelStart(LevelSettingsObject* obj) { + StartPosManager::get()->setStartPositions(m_objects); + if (StartPosManager::get()->isLevelStart()) { + this->setStartPosObject(nullptr); + m_player1->setStartPos(CCPointZero); + m_player1->resetObject(); + m_player2->setStartPos(CCPointZero); + m_player2->resetObject(); + LevelEditorLayer::setupLevelStart(m_levelSettings); + return; + } + auto active = StartPosManager::get()->getActive(); + auto startPos = StartPosManager::get()->getStartPosFromPoint(active); + if (!startPos) { + Notification::create("Couldn't setup Start Position switcher.", CCSprite::createWithSpriteFrameName("edit_delBtnSmall_001.png"))->show(); + StartPosManager::get()->setDefault(); + auto bar = static_cast(m_editorUI->getChildByID("start-pos-button-bar"_spr)); + if (bar) { + bar->setStartPosCounters(); + } + LevelEditorLayer::setupLevelStart(obj); + return; + } + + this->setStartPosObject(startPos); + m_player1->setStartPos(startPos->getPosition()); + m_player1->resetObject(); + m_player2->setStartPos(startPos->getPosition()); + m_player2->resetObject(); + LevelEditorLayer::setupLevelStart(startPos->m_levelSettings); + } +}; + +$onEditorExit { + PlaytestHerePopup::hide(); +} + +// In case $onEditorExit runs on save and play +class $modify(GameManager) { + void returnToLastScene(GJGameLevel* level) { + GameManager::returnToLastScene(level); + StartPosManager::get()->clear(); + } +}; + +class $modify(EditorPauseLayer) { + bool init(LevelEditorLayer* lel) { + if (!EditorPauseLayer::init(lel)) + return false; + + PlaytestHerePopup::hide(); + + return true; + } + + void onSaveAndPlay(CCObject* sender) { + StartPosManager::get()->setStartPositions(m_editorLayer->m_objects); + g_enteringLevel = true; + EditorPauseLayer::onSaveAndPlay(sender); + } + + void onExitEditor(CCObject* sender) { + EditorPauseLayer::onExitEditor(sender); + StartPosManager::get()->clear(); + } + + void onSaveAndExit(CCObject* sender) { + EditorPauseLayer::onSaveAndExit(sender); + StartPosManager::get()->clear(); + } +}; + +class $modify(MyEditorUI, EditorUI) { + StartPosButtonBar* buttonBar = nullptr; + + bool holding = false; + std::unordered_map startPosOriginals; + bool init(LevelEditorLayer* lel) { + if (!EditorUI::init(lel)) { + return false; + } + auto buttonBar = StartPosButtonBar::create(m_editorLayer); + buttonBar->setID("start-pos-button-bar"_spr); + m_fields->buttonBar = buttonBar; + + MoreTabs::get(this)->addEditTab( + "start-pos-border.png"_spr, + buttonBar + ); + return true; + } + + void onDeleteStartPos(CCObject* sender) { + EditorUI::onDeleteStartPos(sender); + StartPosManager::get()->clear(); + StartPosManager::get()->setStartPositions(nullptr); + m_fields->buttonBar->setStartPosCounters(); + } + + void selectObjects(CCArray* objects, bool ignoreFilters) { + EditorUI::selectObjects(objects, ignoreFilters); + PlaytestHerePopup::hide(); + } + + void selectObject(GameObject* obj, bool filter) { + EditorUI::selectObject(obj, filter); + MoreTabs::get(this)->switchEditTab(1); + } + + void moveObject(GameObject* obj, CCPoint pos) { + if (m_editorLayer->m_editorInitialising) { + EditorUI::moveObject(obj, pos); + PlaytestHerePopup::move(); + return; + } + bool movedStartPos = false; + CCPoint original; + if (obj->m_objectID == 31) { + movedStartPos = true; + original = obj->getPosition(); + } + EditorUI::moveObject(obj, pos); + PlaytestHerePopup::move(); + if (movedStartPos) { + // if holding, add to map and recalculate once when touch ends + if (m_fields->holding && !m_fields->startPosOriginals.contains(obj)) { + m_fields->startPosOriginals[obj] = original; + return; + } + if (m_fields->holding) { + return; + } + StartPosManager::get()->replaceStartPos(original, obj->getPosition()); + m_fields->buttonBar->setStartPosCounters(); + } + } + + // doing moving with freemove a lil better + bool ccTouchBegan(CCTouch* touch, CCEvent* event) { + auto ret = EditorUI::ccTouchBegan(touch, event); + m_fields->holding = true; + + // bad fix for bad bug :) + if (m_freeMoveEnabled) { + MoreTabs::get(this, false)->switchEditTab(1); + } + return ret; + } + + // if we are free moving any startpos, update the manager and clear the map + void ccTouchEnded(CCTouch* touch, CCEvent* event) { + EditorUI::ccTouchEnded(touch, event); + if (m_fields->holding && !m_fields->startPosOriginals.empty()) { + for (const auto& kv : m_fields->startPosOriginals) { + StartPosManager::get()->replaceStartPos(kv.second, kv.first->getPosition()); + } + m_fields->buttonBar->setStartPosCounters(); + m_fields->startPosOriginals.clear(); + m_fields->holding = false; + } + } + + void moveObjectCall(EditCommand cmd) { + EditorUI::moveObjectCall(cmd); + PlaytestHerePopup::move(); + } + + void deselectAll() { + EditorUI::deselectAll(); + PlaytestHerePopup::hide(); + } +}; + +class $modify(GameObject) { + void selectObject(ccColor3B color) { + GameObject::selectObject(color); + PlaytestHerePopup::hide(); + if (m_objectID == 31) { + PlaytestHerePopup::show( + LevelEditorLayer::get(), + static_cast(as(this)), + [this]() { + auto buttonBar = static_cast(LevelEditorLayer::get()->m_editorUI->getChildByID("start-pos-button-bar"_spr)); + if (buttonBar) { + buttonBar->setStartPosCounters(); + } + } + ); + } + } +};