|
| 1 | +#pragma once |
| 2 | + |
| 3 | +#include <Geode/Geode.hpp> |
| 4 | +#include <alphalaneous.alphas_geode_utils/include/NodeModding.h> |
| 5 | +#include "HPTParser.hpp" |
| 6 | + |
| 7 | +using namespace geode::prelude; |
| 8 | + |
| 9 | +struct UISchedule { |
| 10 | + std::shared_ptr<HPTNode> m_node; |
| 11 | + Ref<CCAction> m_action; |
| 12 | + |
| 13 | + UISchedule(std::shared_ptr<HPTNode> node, CCAction* action) { |
| 14 | + m_node = node; |
| 15 | + m_action = action; |
| 16 | + } |
| 17 | + |
| 18 | + CCAction* get() { |
| 19 | + return m_action; |
| 20 | + } |
| 21 | +}; |
| 22 | + |
| 23 | +class HPTCCNode; |
| 24 | + |
| 25 | +struct TouchObject : public CCNode, CCTouchDelegate { |
| 26 | + CCNode* m_self; |
| 27 | + bool m_clicked; |
| 28 | + bool m_hovering; |
| 29 | + CCNode* m_parentLayer = nullptr; |
| 30 | + |
| 31 | + std::vector<std::shared_ptr<HPTNode>> m_onClick; |
| 32 | + std::vector<std::shared_ptr<HPTNode>> m_onRelease; |
| 33 | + std::vector<std::shared_ptr<HPTNode>> m_onActivate; |
| 34 | + std::vector<std::shared_ptr<HPTNode>> m_onHover; |
| 35 | + std::vector<std::shared_ptr<HPTNode>> m_onExit; |
| 36 | + |
| 37 | + static TouchObject* create(CCNode* self) { |
| 38 | + auto ret = new TouchObject(); |
| 39 | + if (ret->init(self)) { |
| 40 | + ret->autorelease(); |
| 41 | + return ret; |
| 42 | + } |
| 43 | + delete ret; |
| 44 | + return nullptr; |
| 45 | + } |
| 46 | + |
| 47 | + CCNode* getSceneChildContainingNode() { |
| 48 | + if (m_parentLayer) { |
| 49 | + if (CCScene::get() != m_parentLayer->getParent()) return nullptr; |
| 50 | + return m_parentLayer; |
| 51 | + } |
| 52 | + |
| 53 | + auto current = m_self; |
| 54 | + while (current && current->getParent() != CCScene::get()) { |
| 55 | + current = current->getParent(); |
| 56 | + } |
| 57 | + m_parentLayer = current; |
| 58 | + if (m_parentLayer && CCScene::get() != m_parentLayer->getParent()) return nullptr; |
| 59 | + return current; |
| 60 | + } |
| 61 | + |
| 62 | + bool isLastAlert() { |
| 63 | + bool shouldCheck = false; |
| 64 | + bool lastAlert = false; |
| 65 | + |
| 66 | + if (auto child = getSceneChildContainingNode()) { |
| 67 | + if (!child) return false; |
| 68 | + for (auto c : CCArrayExt<CCNode*>(child->getChildren())) { |
| 69 | + if (!c) continue; |
| 70 | + if (!AlphaUtils::Cocos::hasNode(m_self, c)) { |
| 71 | + shouldCheck = true; |
| 72 | + } |
| 73 | + if (shouldCheck) { |
| 74 | + if (typeinfo_cast<FLAlertLayer*>(c) || typeinfo_cast<CCBlockLayer*>(c)) { |
| 75 | + if (AlphaUtils::Cocos::hasNode(m_self, c)) continue; |
| 76 | + lastAlert = true; |
| 77 | + } |
| 78 | + } |
| 79 | + } |
| 80 | + } |
| 81 | + return lastAlert; |
| 82 | + } |
| 83 | + |
| 84 | + bool isHoverable(CCNode* node, CCPoint point) { |
| 85 | + if (!CCScene::get() || !node || isLastAlert()) return false; |
| 86 | + |
| 87 | + auto sceneChild = getSceneChildContainingNode(); |
| 88 | + if (!sceneChild) return false; |
| 89 | + |
| 90 | + for (auto child : CCArrayExt<CCNode*>(CCScene::get()->getChildren())) { |
| 91 | + if (child->getZOrder() <= sceneChild->getZOrder()) continue; |
| 92 | + if (child->boundingBox().containsPoint(point) && nodeIsVisible(child)) { |
| 93 | + return false; |
| 94 | + } |
| 95 | + } |
| 96 | + return true; |
| 97 | + } |
| 98 | + |
| 99 | + void checkMouse(float) { |
| 100 | + if (!nodeIsVisible(m_self)) return; |
| 101 | + if (!m_self->getParent()) return; |
| 102 | + |
| 103 | + auto worldPos = m_self->convertToWorldSpaceAR(CCPointZero); |
| 104 | + bool isValid = nodeIsVisible(m_self) && m_self->boundingBox().containsPoint(getMousePos()) && isHoverable(m_self, worldPos); |
| 105 | + |
| 106 | + checkTouch(!isValid); |
| 107 | + } |
| 108 | + |
| 109 | + void checkTouch(bool shouldExit) { |
| 110 | + if (!shouldExit && !m_hovering) { |
| 111 | + m_hovering = true; |
| 112 | + parseForEach(m_onHover); |
| 113 | + } |
| 114 | + if (shouldExit && m_hovering) { |
| 115 | + m_hovering = false; |
| 116 | + parseForEach(m_onExit); |
| 117 | + } |
| 118 | + } |
| 119 | + |
| 120 | + bool init(CCNode* self) { |
| 121 | + m_self = self; |
| 122 | + schedule(schedule_selector(TouchObject::checkMouse)); |
| 123 | + return true; |
| 124 | + } |
| 125 | + |
| 126 | + void parseForEach(std::vector<std::shared_ptr<HPTNode>> vec) { |
| 127 | + for (const auto& v : vec) { |
| 128 | + v->reparse(); |
| 129 | + } |
| 130 | + } |
| 131 | + |
| 132 | + bool ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent) override { |
| 133 | + if (m_self->boundingBox().containsPoint(pTouch->getLocation()) && getSceneChildContainingNode()) { |
| 134 | + parseForEach(m_onClick); |
| 135 | + m_clicked = true; |
| 136 | + return true; |
| 137 | + } |
| 138 | + return false; |
| 139 | + } |
| 140 | + |
| 141 | + void ccTouchMoved(CCTouch *pTouch, CCEvent *pEvent) override { |
| 142 | + if (m_self->boundingBox().containsPoint(pTouch->getLocation()) && getSceneChildContainingNode()) { |
| 143 | + if (!m_clicked) { |
| 144 | + m_clicked = true; |
| 145 | + parseForEach(m_onClick); |
| 146 | + } |
| 147 | + } |
| 148 | + else { |
| 149 | + if (m_clicked) { |
| 150 | + m_clicked = false; |
| 151 | + parseForEach(m_onRelease); |
| 152 | + } |
| 153 | + } |
| 154 | + } |
| 155 | + |
| 156 | + void ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent) override { |
| 157 | + m_clicked = false; |
| 158 | + parseForEach(m_onActivate); |
| 159 | + parseForEach(m_onRelease); |
| 160 | + } |
| 161 | + |
| 162 | + void ccTouchCancelled(CCTouch *pTouch, CCEvent *pEvent) override { |
| 163 | + m_clicked = false; |
| 164 | + parseForEach(m_onRelease); |
| 165 | + } |
| 166 | + |
| 167 | + void onEnter() override { |
| 168 | + CCNode::onEnter(); |
| 169 | + CCDirector::get()->getTouchDispatcher()->addTargetedDelegate(this, 0, true); |
| 170 | + } |
| 171 | + |
| 172 | + void onExit() override { |
| 173 | + CCNode::onExit(); |
| 174 | + CCDirector::get()->getTouchDispatcher()->removeDelegate(this); |
| 175 | + } |
| 176 | + |
| 177 | + ~TouchObject() { |
| 178 | + unscheduleAllSelectors(); |
| 179 | + } |
| 180 | +}; |
| 181 | + |
| 182 | +class $nodeModify(HPTCCNode, CCNode) { |
| 183 | + |
| 184 | + struct Fields { |
| 185 | + TouchObject* m_touchObject; |
| 186 | + std::vector<std::shared_ptr<HPTNode>> m_owned; |
| 187 | + std::vector<std::shared_ptr<HPTNode>> m_resetNodes; |
| 188 | + std::vector<std::shared_ptr<UISchedule>> m_schedules; |
| 189 | + }; |
| 190 | + |
| 191 | + static void resetAllFromPack(const std::string& packName) { |
| 192 | + auto scene = CCScene::get(); |
| 193 | + if (!scene) return; |
| 194 | + resetChildren(packName, scene); |
| 195 | + } |
| 196 | + |
| 197 | + static void resetChildren(const std::string& packName, CCNode* child) { |
| 198 | + reinterpret_cast<HPTCCNode*>(child)->resetByPack(packName); |
| 199 | + for (auto node : CCArrayExt<CCNode*>(child->getChildren())) { |
| 200 | + resetChildren(packName, node); |
| 201 | + } |
| 202 | + } |
| 203 | + |
| 204 | + void modify() {} |
| 205 | + |
| 206 | + void setOwner(std::shared_ptr<HPTNode> node) { |
| 207 | + m_fields->m_owned.push_back(node); |
| 208 | + } |
| 209 | + |
| 210 | + void enableTouch() { |
| 211 | + auto fields = m_fields.self(); |
| 212 | + if (!fields->m_touchObject) { |
| 213 | + fields->m_touchObject = TouchObject::create(this); |
| 214 | + addChild(fields->m_touchObject); |
| 215 | + } |
| 216 | + } |
| 217 | + |
| 218 | + void resetByPack(const std::string& packName) { |
| 219 | + auto fields = m_fields.self(); |
| 220 | + |
| 221 | + auto erase = [&packName] (std::vector<std::shared_ptr<HPTNode>>& vec) { |
| 222 | + vec.erase( |
| 223 | + std::remove_if(vec.begin(), vec.end(), [&packName](std::shared_ptr<HPTNode> node){ return node->packName == packName; }), |
| 224 | + vec.end() |
| 225 | + ); |
| 226 | + }; |
| 227 | + |
| 228 | + for (auto action : fields->m_schedules) { |
| 229 | + if (action->m_node->packName == packName) { |
| 230 | + stopAction(action->get()); |
| 231 | + } |
| 232 | + } |
| 233 | + |
| 234 | + fields->m_schedules.erase( |
| 235 | + std::remove_if(fields->m_schedules.begin(), fields->m_schedules.end(), [&packName] (std::shared_ptr<UISchedule> action) { return action->m_node->packName == packName; }), |
| 236 | + fields->m_schedules.end() |
| 237 | + ); |
| 238 | + |
| 239 | + fields->m_owned.erase( |
| 240 | + std::remove_if(fields->m_owned.begin(), fields->m_owned.end(), [&packName] (std::shared_ptr<HPTNode> node) { return node->packName == packName; }), |
| 241 | + fields->m_owned.end() |
| 242 | + ); |
| 243 | + |
| 244 | + if (fields->m_touchObject) { |
| 245 | + erase(fields->m_touchObject->m_onClick); |
| 246 | + erase(fields->m_touchObject->m_onRelease); |
| 247 | + erase(fields->m_touchObject->m_onActivate); |
| 248 | + erase(fields->m_touchObject->m_onHover); |
| 249 | + erase(fields->m_touchObject->m_onExit); |
| 250 | + } |
| 251 | + } |
| 252 | + |
| 253 | + void setSchedule(std::shared_ptr<HPTNode> node) { |
| 254 | + auto fields = m_fields.self(); |
| 255 | + CCActionInstant* callFunc = CallFuncExt::create([node] { |
| 256 | + |
| 257 | + }); |
| 258 | + CCDelayTime* delay = CCDelayTime::create(0 /*pass in from node*/); |
| 259 | + CCSequence* sequence = CCSequence::create(callFunc, delay, nullptr); |
| 260 | + CCRepeatForever* repeat = CCRepeatForever::create(sequence); |
| 261 | + |
| 262 | + auto schedule = std::make_shared<UISchedule>(node, repeat); |
| 263 | + |
| 264 | + fields->m_schedules.push_back(schedule); |
| 265 | + } |
| 266 | + |
| 267 | + void setOnClick(std::shared_ptr<HPTNode> node) { |
| 268 | + auto fields = m_fields.self(); |
| 269 | + enableTouch(); |
| 270 | + fields->m_touchObject->m_onClick.push_back(node); |
| 271 | + } |
| 272 | + |
| 273 | + void setOnRelease(std::shared_ptr<HPTNode> node) { |
| 274 | + auto fields = m_fields.self(); |
| 275 | + enableTouch(); |
| 276 | + fields->m_touchObject->m_onRelease.push_back(node); |
| 277 | + } |
| 278 | + |
| 279 | + void setOnActivate(std::shared_ptr<HPTNode> node) { |
| 280 | + auto fields = m_fields.self(); |
| 281 | + enableTouch(); |
| 282 | + fields->m_touchObject->m_onActivate.push_back(node); |
| 283 | + } |
| 284 | + |
| 285 | + void setOnHover(std::shared_ptr<HPTNode> node) { |
| 286 | + auto fields = m_fields.self(); |
| 287 | + enableTouch(); |
| 288 | + fields->m_touchObject->m_onHover.push_back(node); |
| 289 | + } |
| 290 | + |
| 291 | + void setOnExit(std::shared_ptr<HPTNode> node) { |
| 292 | + auto fields = m_fields.self(); |
| 293 | + enableTouch(); |
| 294 | + fields->m_touchObject->m_onExit.push_back(node); |
| 295 | + } |
| 296 | +}; |
| 297 | + |
0 commit comments