// // ToyDragAndDropHandler.cpp // SteveMaggieCpp // // Created by Katarzyna Kalinowska-Górska on 02.06.2017. // // #include #include "ToyDragAndDropHandler.h" #include "ToyMiscUtils.h" #include "ToySpriteAnimator.h" #include "ToyGeometryUtils.h" ToyDragAndDropHandler::ToyDragAndDropHandler(std::vector objectsToDrag, cocos2d::Node* destinationObject, std::vector wrongDraggedObjects, std::vector wrongDestinationObjects) { _objectsToDrag = objectsToDrag; _destinationObject = destinationObject; _wrongDraggedObjects = wrongDraggedObjects; _wrongDestinationObjects = wrongDestinationObjects; _extendPercentValues = std::vector(_objectsToDrag.size(), 0); _draggedObject = NULL; _allowDraggingWrongObjects = false; _allowDraggingInvisibleObjects = false; _allowMultipleDrag = false; _moreAccurateDestinationCheck = false; _fingerOnlyDestinationCheck = false; _currentDraggingState = NotDragging; _draggingWrongObject = false; _draggingOverWrongDestinationObject = NULL; _finalSuccessCallback = [](cocos2d::Node*, cocos2d::Node*){}; _successCallback = [](cocos2d::Node*, cocos2d::Node*){}; _wrongDestinationObjectCallback = [](cocos2d::Node*){}; _startDraggingWrongObjectCallback = [](cocos2d::Node*){}; _startDraggingCorrectObjectCallback = [](cocos2d::Node*){}; _draggedObjectEnterCorrectDestinationCallback = [](cocos2d::Node*, cocos2d::Node*){}; _draggedObjectEnterWrongDestinationCallback = [](cocos2d::Node*, cocos2d::Node*){}; _dropTooSoonCallback = [](){}; _draggedWrongObjectCallback = [](cocos2d::Node*){}; _objectDragNDropSuccessBehaviour = [](cocos2d::Node*, cocos2d::Node*, cocos2d::Point){}; } void ToyDragAndDropHandler::setObjectToDragExtendPercentValues(std::vector values) { _extendPercentValues = values; } void ToyDragAndDropHandler::setAccurateDestinationCheck(bool accurateDstCheck) { _moreAccurateDestinationCheck = accurateDstCheck; } void ToyDragAndDropHandler::setFingerOnlyDestinationCheck(bool fingerOnlyDstCheck) { _fingerOnlyDestinationCheck = fingerOnlyDstCheck; } TouchHandlerFunction ToyDragAndDropHandler::prepareTouchBeganHandler() { return [&](cocos2d::Touch* touch, cocos2d::Event* event){ for(int d = 0; d < _objectsToDrag.size(); ++d){ if(!_allowDraggingInvisibleObjects && _objectsToDrag[d]->getOpacity() == 0){ continue; } auto grabArea = ToyMiscUtils::getExtendedActiveArea(_objectsToDrag[d], _extendPercentValues[d], _extendPercentValues[d]); if(grabArea.containsPoint(touch->getLocation())){ _draggedObject = _objectsToDrag[d]; _draggedObjectOriginalZ = _draggedObject->getLocalZOrder();//getZOrder(); _startLocation = touch->getLocation(); _startingObjectPosition = _draggedObject->getPosition(); for(int i = ToySpriteAnimator::RF_ANIMATION_TAG_RANGE_MIN; i <= ToySpriteAnimator::RF_ANIMATION_TAG_RANGE_MAX; ++i){ auto action = _draggedObject->getActionByTag(i); if(action){ _cachedDraggedObjectRFActionTags.push_back(i); } } _draggedObject->stopAllActions(); _draggedObject->getParent()->reorderChild(_draggedObject, 100); _startDraggingCorrectObjectCallback(_draggedObject); // auto it = _startDraggingConcreteObjectCallbacks.find(dynamic_cast(_draggedObject)->scenarioObjectName); // if(it != _startDraggingConcreteObjectCallbacks.end()){ // it->second(_draggedObject); // } _currentDraggingState = Dragging; _draggingWrongObject = false; return true; } } // if no object found cocos2d::Node* wrongObj = NULL; for(int i = 0; i < _wrongDraggedObjects.size(); ++i){ //todo use extended grab area? if(!_allowDraggingInvisibleObjects && _wrongDraggedObjects[i]->getOpacity() == 0){ continue; } if(ToyGeometryUtils::getBoundingBoxToWorld(_wrongDraggedObjects[i]).containsPoint(touch->getLocation())){ if(wrongObj == nullptr || wrongObj->getLocalZOrder() < _wrongDraggedObjects[i]->getLocalZOrder()){ wrongObj = _wrongDraggedObjects[i]; } } } if(wrongObj){ _startDraggingWrongObjectCallback(wrongObj); if(_allowDraggingWrongObjects){ _draggedObject = wrongObj; _draggedObjectOriginalZ = _draggedObject->getLocalZOrder(); _startLocation = touch->getLocation(); _startingObjectPosition = _draggedObject->getPosition(); // auto it = _startDraggingConcreteObjectCallbacks.find(dynamic_cast(_draggedObject)->scenarioObjectName); // if(it != _startDraggingConcreteObjectCallbacks.end()){ // it->second(_draggedObject); // } for(int i = ToySpriteAnimator::RF_ANIMATION_TAG_RANGE_MIN; i <= ToySpriteAnimator::RF_ANIMATION_TAG_RANGE_MAX; ++i){ auto action = _draggedObject->getActionByTag(i); if(action){ _cachedDraggedObjectRFActionTags.push_back(i); } } _draggedObject->stopAllActions(); _draggedObject->getParent()->reorderChild(_draggedObject, 100); _currentDraggingState = Dragging; _draggingWrongObject = true; return true; } } return false; }; } TouchHandlerFunction ToyDragAndDropHandler::prepareTouchMovedHandler() { return [&](cocos2d::Touch* touch, cocos2d::Event* event){ if(_draggedObject){ auto dx = touch->getLocation().x - _startLocation.x; auto dy = touch->getLocation().y - _startLocation.y; auto newPosition = cocos2d::Point(_startingObjectPosition.x + dx, _startingObjectPosition.y + dy); _draggedObject->setPosition(newPosition); // printf("POSX: %f POSY: %f\n", (_draggedObject->getPositionX()-_draggedObject->getParent()->getBoundingBox().size.width/2)/1.33/*-_draggedObject->getBoundingBox().size.width/2*/, (_draggedObject->getPositionY() - _draggedObject->getParent()->getBoundingBox().size.height/2)/1.33); if(_moreAccurateDestinationCheck){ bool isRightObject, isWrongObject; auto destinationObject = this->detectDestination(touch->getLocation(), isRightObject, isWrongObject); if(isRightObject){ this->onMovingOverCorrectDestination(); } else if(isWrongObject){ this->onMovingOverWrongDestination(destinationObject); } else { this->onMovingOverNothing(); } } else if(this->checkDraggingOverCorrectDestination(touch->getLocation())){ this->onMovingOverCorrectDestination(); } else { auto wrongDstObj = this->checkDraggingOverWrongDestination(touch->getLocation()); if(wrongDstObj){ this->onMovingOverWrongDestination(wrongDstObj); } else { this->onMovingOverNothing(); } } } }; } void ToyDragAndDropHandler::onMovingOverCorrectDestination() { if(_currentDraggingState != DraggingOverCorrectDestination){ _draggedObjectEnterCorrectDestinationCallback(_draggedObject, _destinationObject); } _currentDraggingState = DraggingOverCorrectDestination; } void ToyDragAndDropHandler::onMovingOverWrongDestination(cocos2d::Node* wrongDestinationObject) { if((_currentDraggingState != DraggingOverWrongDestination || (_currentDraggingState == DraggingOverWrongDestination && _draggingOverWrongDestinationObject != wrongDestinationObject))) { _draggedObjectEnterWrongDestinationCallback(_draggedObject, wrongDestinationObject); } _currentDraggingState = DraggingOverWrongDestination; _draggingOverWrongDestinationObject = wrongDestinationObject; } void ToyDragAndDropHandler::onMovingOverNothing() { _currentDraggingState = Dragging; _draggingOverWrongDestinationObject = NULL; } TouchHandlerFunction ToyDragAndDropHandler::prepareTouchEndedHandler() //todo tu dokonczyc case kiedy dragujemy zly obiekt { return [&](cocos2d::Touch* touch, cocos2d::Event* event){ if(_draggedObject){ if(_moreAccurateDestinationCheck){ bool isRightObject, isWrongObject; auto destinationObject = this->detectDestination(touch->getLocation(), isRightObject, isWrongObject); if(isRightObject){ this->onTouchEndedOverCorrectDestination(); } else if(isWrongObject){ _wrongDestinationObjectCallback(destinationObject); this->draggedObjectMoveBack(true); } else { _dropTooSoonCallback(); this->draggedObjectMoveBack(true); } } else if(this->checkDraggingOverCorrectDestination(touch->getLocation())){ this->onTouchEndedOverCorrectDestination(); } else { this->draggedObjectMoveBack(true); auto wrongDstObj = this->checkDraggingOverWrongDestination(touch->getLocation()); if(wrongDstObj){ _wrongDestinationObjectCallback(wrongDstObj); } else { _dropTooSoonCallback(); } } _draggedObject = NULL; } _currentDraggingState = NotDragging; }; } void ToyDragAndDropHandler::onTouchEndedOverCorrectDestination() { if(_draggingWrongObject){ _draggedWrongObjectCallback(_draggedObject); this->draggedObjectMoveBack(true); } else { _objectDragNDropSuccessBehaviour(_destinationObject, _draggedObject, _startingObjectPosition); if(_allowMultipleDrag){ this->draggedObjectMoveBack(true); } else { auto iter = std::find(_objectsToDrag.begin(), _objectsToDrag.end(), _draggedObject); _objectsToDrag.erase(iter); } if(_objectsToDrag.size() > 0){ _successCallback(_destinationObject, _draggedObject); } else { _finalSuccessCallback(_destinationObject, _draggedObject); } } _draggedObject = NULL; } cocos2d::Node* ToyDragAndDropHandler::detectDestination(const cocos2d::Point& touchLocation, bool& isRightObject, bool& isWrongObject) { auto rightDestinationIntersection = ToyGeometryUtils::calculateIntersectionPercentage(ToyGeometryUtils::getBoundingBoxToWorld(_draggedObject), ToyGeometryUtils::getBoundingBoxToWorld(_destinationObject)); float greatestWrongObjectIntersection = 0; cocos2d::Node* bestIntersectingWrongObject = NULL; for(int i = 0; i < _wrongDestinationObjects.size(); ++i){ auto wrongObjectIntersection = ToyGeometryUtils::calculateIntersectionPercentage(ToyGeometryUtils::getBoundingBoxToWorld(_draggedObject), ToyGeometryUtils::getBoundingBoxToWorld(_wrongDestinationObjects[i]), 1); if(wrongObjectIntersection > greatestWrongObjectIntersection){ greatestWrongObjectIntersection = wrongObjectIntersection; bestIntersectingWrongObject = _wrongDestinationObjects[i]; } } cocos2d::Node* destinationObject = NULL; isRightObject = false; isWrongObject = false; if(rightDestinationIntersection >= greatestWrongObjectIntersection && rightDestinationIntersection > 0){ destinationObject = _destinationObject; isRightObject = true; } else if(greatestWrongObjectIntersection != 0){ destinationObject = bestIntersectingWrongObject; isWrongObject = true; } return destinationObject; } bool ToyDragAndDropHandler::checkDraggingOverCorrectDestination(const cocos2d::Point& touchLocation) { return _draggedObject != NULL && ((!_fingerOnlyDestinationCheck && ToyGeometryUtils::getBoundingBoxToWorld(_destinationObject).intersectsRect(ToyGeometryUtils::getBoundingBoxToWorld(_draggedObject))) || (_fingerOnlyDestinationCheck && ToyGeometryUtils::getBoundingBoxToWorld(_destinationObject).containsPoint(touchLocation))); } cocos2d::Node* ToyDragAndDropHandler::checkDraggingOverWrongDestination(const cocos2d::Point& touchLocation) { if(!_draggedObject){ return NULL; } else { cocos2d::Node* wrongDstObj = nullptr; for(int i = 0; i < _wrongDestinationObjects.size(); ++i){ if(ToyGeometryUtils::getBoundingBoxToWorld(_wrongDestinationObjects[i]).intersectsRect(ToyGeometryUtils::getBoundingBoxToWorld(_draggedObject))){ wrongDstObj = _wrongDestinationObjects[i]; break; } } return wrongDstObj; } } void ToyDragAndDropHandler::draggedObjectMoveBack(bool restoreActions) { if(_draggedObject){ _draggedObject->runAction(cocos2d::Sequence::create(cocos2d::MoveTo::create(0.2, _startingObjectPosition), cocos2d::CallFunc::create(std::bind([&](cocos2d::Node* object, int zOrder){ object->getParent()->reorderChild(object, zOrder); // printf("z order: %d", zOrder); }, _draggedObject, _draggedObjectOriginalZ)), nullptr)); if(restoreActions){ for(int i = 0; i < _cachedDraggedObjectRFActionTags.size(); ++i){ int actionTag = _cachedDraggedObjectRFActionTags[i]; ToySpriteAnimator::restoreRFAction(_draggedObject, actionTag); } } _cachedDraggedObjectRFActionTags.clear(); } }