// // ToyParentScene.cpp // SteveMaggieCpp // // Created by Katarzyna Kalinowska-Górska on 16.05.2017. // // #include #include "ToyParentScene.h" #include "ToyLayoutParser.h" #include "ToySimpleButton.h" #include "ToyTwoStateButton.h" #include "ToyMathUtils.h" #include "ToyStringUtils.h" #include "ToyResourceUtilities.h" #include "ToyMiscUtils.h" #include "ToyTOSAcceptPopupView.h" #include "ToyLevelPickerLayer.h" #include "ToySoundsRepo.h" #include "HelloWorldScene.h" ToyParentScene* ToyParentScene::create(std::string layoutFilePath, std::string scenarioFilePath) { ToyParentScene * scene = new (std::nothrow) ToyParentScene(); if(scene && scene->initWithConfigurationFiles(layoutFilePath, scenarioFilePath)) { scene->autorelease(); return scene; } CC_SAFE_DELETE(scene); return nullptr; } bool ToyParentScene::initWithConfigurationFiles(std::string layoutFilePath, std::string scenarioFilePath) { if(!ToySceneWithUtils::init()) { return false; } _layoutFilePath = layoutFilePath; _scenarioFilePath = scenarioFilePath; _loadFromAssets = false; _layers.clear(); _objects.clear(); _alwaysSkipDemo = false; _layoutLoaded = false; _scenarioHandler = new ToyScenarioHandler(this); _scenarioObjects.insert({"scenarioHandler", _scenarioHandler}); _userTaskInteractionStarted = false; _currentTask = ""; _rightObjectTouchedAlready = false; //TODO these should probably be in the ToyWorksheetScene _isHandlingTouches = false; this->clearTouchHandlers(); this->resetLastUserTouchTimes(); return true; } void ToyParentScene::reloadLayoutClean() { this->removeAllChildren(); _layers.clear(); _objects.clear(); _scenarioObjects.clear(); _scenarioObjects.insert({"scenarioHandler", _scenarioHandler}); _userTaskInteractionStarted = false; _currentTask = ""; _rightObjectTouchedAlready = false; //TODO these should probably be in the ToyWorksheetScene _isHandlingTouches = false; this->clearTouchHandlers(); this->resetLastUserTouchTimes(); this->loadLayout(true); } void ToyParentScene::loadLayout(bool forceLoad) { if(!this->_layoutLoaded || forceLoad){ _layoutLoaded = false; if (_loadFromAssets && cocos2d::FileUtils::getInstance()->isFileExist(this->_layoutFilePath)) { auto& layoutParser = ToyLayoutParser::getInstance(); layoutParser.loadLayoutFromJSONFile(this->_layoutFilePath, this); this->_layoutLoaded = true; } else if(!_loadFromAssets){ auto& layoutParser = ToyLayoutParser::getInstance(); layoutParser.loadLayoutFromDownloadedJSONFile(this->_layoutFilePath, this); this->_layoutLoaded = true; } } } void ToyParentScene::loadScenario(bool skipDemoActions) { auto fileUtils = cocos2d::FileUtils::getInstance(); if (fileUtils->isFileExist(this->_scenarioFilePath)) { std::string jsonString = fileUtils->getStringFromFile(this->_scenarioFilePath); _scenarioHandler->loadScenarioFromJSONString(jsonString, skipDemoActions); } } void ToyParentScene::setLoadFromAssets(bool loadFromAssets) { _loadFromAssets = loadFromAssets; } // overrides void ToyParentScene::onEnter() { this->configureTouchHandlers(); this->configureTransitionListeners(); ToySceneWithUtils::onEnter(); // auto playing = this->playBackgroundMusic(true); // this->adjustBgMusicButton(playing); } void ToyParentScene::onExit() { ToySceneWithUtils::onExit(); if(_scenarioHandler){ _scenarioHandler->end(); // delete _scenarioHandler; //TODO do it somewhere when the scene gets dealloced } // this->stopBackgroundMusic(false); } void ToyParentScene::configureTransitionListeners() //TODO check if it works. remove somewhere later the vent listeners? leaks? { _eventDispatcher->addCustomEventListener("game_on_hide", CC_CALLBACK_1(ToyParentScene::onGameHide, this)); _eventDispatcher->addCustomEventListener("game_on_show", CC_CALLBACK_1(ToyParentScene::onGameShow, this)); } void ToyParentScene::onGameHide(cocos2d::EventCustom* event) { _scenarioHandler->pauseScenario(); } void ToyParentScene::onGameShow(cocos2d::EventCustom* event) { _scenarioHandler->resumeScenario(); } // object config & handlers ToyScenarioObject* ToyParentScene::getObjectByName(std::string objectName) { if(_objects.find(objectName) == _objects.end()){ if(_scenarioObjects.find(objectName) == _scenarioObjects.end()){ return NULL; } return _scenarioObjects.at(objectName); } return dynamic_cast(_objects.at(objectName)); } void ToyParentScene::addNewObject(std::string objectName, ToyScenarioObject* newObject) { _scenarioObjects.insert({objectName, newObject}); } void ToyParentScene::setupBackgroundMusic(std::string backgroundMusicPath) { _backgroundMusicFilePath = backgroundMusicPath; } bool ToyParentScene::touchHandlerForWidget(std::string objectName, cocos2d::ui::Widget::TouchEventType touchEventType) { if(objectName == "backButton" && touchEventType == cocos2d::ui::Widget::TouchEventType::ENDED){ _scenarioHandler->pauseScenario(); _scenarioHandler->stopAllActions(); ToySoundsRepo::stopAllSounds(); cocos2d::Director::getInstance()->popScene(); _sceneDismissCallback(); return true; } /*else if(objectName == "backgroundMusicButton" && touchEventType == cocos2d::ui::Widget::TouchEventType::ENDED){ // auto bgMusicButton = dynamic_cast(_objects[objectName]); // if(this->isBackgroundMusicPlaying()){ // this->stopBackgroundMusic(true); // // if(bgMusicButton->isActive()){ // bgMusicButton->setActive(false); // } // // } else { // this->playBackgroundMusic(false); // // if(!bgMusicButton->isActive()){ // bgMusicButton->setActive(true); // } // } }*/ return false; } void ToyParentScene::adjustBgMusicButton(bool bgMusicPlaying) { if(_objects.find("backgroundMusicButton") != _objects.end()){ auto bgMusicButton = dynamic_cast(_objects["backgroundMusicButton"]); if(bgMusicPlaying){ if(!bgMusicButton->isActive()){ bgMusicButton->setActive(true); } } else { if(bgMusicButton->isActive()){ bgMusicButton->setActive(false); } } } } void ToyParentScene::disableButton(std::string buttonName) { auto object = this->getObjectByName(buttonName); auto button = dynamic_cast(object); if(button){ button->setEnabled(false); if(button->getOpacity() != 0){ button->setOpacity(128); for(auto child : button->getChildren()){ child->setOpacity(128); } } } } void ToyParentScene::enableButton(std::string buttonName) { auto object = this->getObjectByName(buttonName); auto button = dynamic_cast(object); if(button){ button->setEnabled(true); button->setOpacity(255); for(auto child : button->getChildren()){ child->setOpacity(255); } } } // fast forward button convenience void ToyParentScene::disableFastForwardButton() { this->disableButton("fastForwardButton"); } void ToyParentScene::enableFastForwardButton() { this->enableButton("fastForwardButton"); } // touch handlers void ToyParentScene::configureTouchHandlers() { auto touchListener = cocos2d::EventListenerTouchOneByOne::create(); touchListener->onTouchBegan = [&](cocos2d::Touch* touch, cocos2d::Event* event) -> bool{ _isHandlingTouches = true; for(auto handler : _touchBeganHandlers){ handler.second(touch, event); } _isHandlingTouches = false; this->filterTouchHandlers(); return true; }; touchListener->onTouchMoved = [&](cocos2d::Touch* touch, cocos2d::Event* event){ _isHandlingTouches = true; _lastUserTouchMovedTime = cocos2d::utils::getTimeInMilliseconds(); for(auto handler : _touchMovedHandlers){ handler.second(touch, event); } _isHandlingTouches = false; this->filterTouchHandlers(); }; touchListener->onTouchEnded = [&](cocos2d::Touch* touch, cocos2d::Event* event){ _isHandlingTouches = true; _lastUserTouchEndedTime = cocos2d::utils::getTimeInMilliseconds(); for(auto handler : _touchEndedHandlers){ handler.second(touch, event); } _isHandlingTouches = false; this->filterTouchHandlers(); }; _eventDispatcher->addEventListenerWithSceneGraphPriority(touchListener, this); } void ToyParentScene::filterTouchHandlers() { for(auto pair : _touchHandlersToRemove){ this->removeTouchHandler(pair.first, pair.second); } _touchHandlersToRemove.clear(); } void ToyParentScene::clearTouchHandlers() { _touchBeganHandlers.clear(); _touchMovedHandlers.clear(); _touchEndedHandlers.clear(); } void ToyParentScene::addTouchHandler(std::string key, TouchHandlerFunction touchHandler, TouchHandlerType touchType) { auto handlersArray = this->pickTouchHandlersContainer(touchType); if(handlersArray){ handlersArray->insert({key, touchHandler}); } } void ToyParentScene::removeTouchHandler(std::string touchHandlerKey, TouchHandlerType touchHandlerType) { if(!_isHandlingTouches){ auto handlersArray = this->pickTouchHandlersContainer(touchHandlerType); if(handlersArray){ handlersArray->erase(touchHandlerKey); } } else { _touchHandlersToRemove.insert({touchHandlerKey, touchHandlerType}); } } std::map* ToyParentScene::pickTouchHandlersContainer(TouchHandlerType touchHandlerType) { std::map* handlersArray = NULL; switch(touchHandlerType){ case TouchHandlerType::TOUCHES_BEGAN: handlersArray = &_touchBeganHandlers; break; case TouchHandlerType::TOUCHES_MOVED: handlersArray = &_touchMovedHandlers; break; case TouchHandlerType::TOUCHES_ENDED: handlersArray = &_touchEndedHandlers; break; default: break; } return handlersArray; } long long ToyParentScene::getLastScreenTouchTime() { if(_lastUserTouchEndedTime != -1 && _lastUserTouchMovedTime != -1){ return MAX(_lastUserTouchEndedTime, _lastUserTouchMovedTime); } else { return _lastUserTouchMovedTime; // if moved is null, ended must be null as well } } void ToyParentScene::resetLastUserTouchTimes() { _lastUserTouchEndedTime = -1; _lastUserTouchMovedTime = -1; } void ToyParentScene::setAlwaysSkipDemo(bool alwaysSkipDemo) { _alwaysSkipDemo = alwaysSkipDemo; } // layout parse delegate void ToyParentScene::addLayer(cocos2d::Layer* layer) { _layers.push_back(layer); } void ToyParentScene::addObject(std::string objectName, cocos2d::Node* object) { _objects.insert({objectName, object}); } void ToyParentScene::addToyScenarioObject(std::string objectName, ToyScenarioObject* object) { _scenarioObjects.insert({objectName, object}); } // ToyScenarioObject void ToyParentScene::setProperty(std::string propertyName, const rapidjson::Value& newValue, ActionParseDelegate* parseDelegate) { static std::string demoState = "demoState"; static std::string userTaskInteractionStarted = "userTaskInteractionStarted"; static std::string currentTask = "currentTask"; static std::string rightObjectTouchedAlready = "rightObjectTouchedAlready"; if (propertyName == demoState) { _scenarioHandler->setProperty(propertyName, newValue, parseDelegate); } else if(propertyName == userTaskInteractionStarted){ _userTaskInteractionStarted = newValue.GetBool(); } else if(propertyName.find(userTaskInteractionStarted) == 0){ std::string additionalName = propertyName.substr(userTaskInteractionStarted.size()); _userTaskInteractionStartedMap[additionalName] = newValue.GetBool(); } else if(propertyName == currentTask){ _currentTask = newValue.GetString(); } else if(propertyName == rightObjectTouchedAlready){ _rightObjectTouchedAlready = newValue.GetBool(); } } void ToyParentScene::callFunctionByName(std::string methodName, const rapidjson::Value* arguments, ActionParseDelegate* parseDelegate, std::function callback) { if(methodName == "disableFastForwardButton"){ this->disableFastForwardButton(); } else if(methodName == "clearTouchHandlers"){ this->clearTouchHandlers(); } } std::string ToyParentScene::getPropertyAsString(std::string propertyName) { static std::string userTaskInteractionStarted = "userTaskInteractionStarted"; if(propertyName == userTaskInteractionStarted){ return _userTaskInteractionStarted ? "true" : "false"; } else if(propertyName.find(userTaskInteractionStarted) != std::string::npos){ auto tokens = ToyStringUtils::splitString(propertyName, userTaskInteractionStarted); if(tokens.size() > 0){ auto key = tokens[tokens.size()-1]; if(_userTaskInteractionStartedMap.find(key) != _userTaskInteractionStartedMap.end()){ return _userTaskInteractionStartedMap.at(key) ? "true" : "false"; } } } else if(propertyName == "rightObjectTouchedAlready"){ return _rightObjectTouchedAlready ? "true" : "false"; } return "NULL"; } void ToyParentScene::addNewNodeToObjectLayer(cocos2d::Node* newNode){ if(_layers.size() > 0){ _layers[_layers.size()-1]->addChild(newNode); } } void ToyParentScene::showTOSAcceptPopup(std::function onAccept){ if(ToyMiscUtils::wasTOSAccepted()){ // gameState->tosAcceptMenuShown = false; onAccept(); } else { // gameState->tosAcceptMenuShown = true; auto tosPopup = ToyTOSAcceptPopupView::create(std::bind([&](std::functionf){ ToyMiscUtils::saveTOSAccepted(); // gameState->tosAcceptMenuShown = false; f();}, onAccept)); addTouchBlockingLayer(tosPopup); tosPopup->setLocalZOrder(500); #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) auto keyboardListener = cocos2d::EventListenerKeyboard::create(); keyboardListener->onKeyReleased = std::bind([&](cocos2d::EventKeyboard::KeyCode keyCode, cocos2d::Event* event, ToyTOSAcceptPopupView* n){ if(keyCode == cocos2d::EventKeyboard::KeyCode::KEY_BACK){ if(ToyMiscUtils::isNodeVisible(n)){ if (n->isShowingParentalGate()) { n->hideParentalGate(); } else { cocos2d::Director::getInstance()->end(); //auto scene = HelloWorld::createScene(); //cocos2d::Director::getInstance()->replaceScene(scene); } } } }, std::placeholders::_1, std::placeholders::_2, tosPopup); _eventDispatcher->addEventListenerWithSceneGraphPriority(keyboardListener, tosPopup); #endif } } void ToyParentScene::showToyLevelPickerLayer(std::function onLayerDismissed){ isShowingLevelPicker = true; auto levelPickerLayer = ToyLevelPickerLayer::create(getBoundingBox().size.width, getBoundingBox().size.height); addTouchBlockingLayer(levelPickerLayer); levelPickerLayer->setLocalZOrder(400); levelPickerLayer->addOnGoPressedCallback(onLayerDismissed); #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) auto keyboardListener = cocos2d::EventListenerKeyboard::create(); keyboardListener->onKeyReleased = std::bind([&](cocos2d::EventKeyboard::KeyCode keyCode, cocos2d::Event* event, cocos2d::Node* n){ if(keyCode == cocos2d::EventKeyboard::KeyCode::KEY_BACK){ if(ToyMiscUtils::isNodeVisible(n)){ cocos2d::Director::getInstance()->end(); //auto scene = HelloWorld::createScene(); //cocos2d::Director::getInstance()->replaceScene(scene); } } }, std::placeholders::_1, std::placeholders::_2, levelPickerLayer); _eventDispatcher->addEventListenerWithSceneGraphPriority(keyboardListener, levelPickerLayer); #endif } void ToyParentScene::addTouchBlockingLayer(cocos2d::Node* layer){ addChild(layer); auto touchListener = cocos2d::EventListenerTouchOneByOne::create(); touchListener->setSwallowTouches(true); touchListener->onTouchBegan = std::bind([&](cocos2d::Touch* touch, cocos2d::Event* event, cocos2d::Node* n){ return ToyMiscUtils::isNodeVisible(n); }, std::placeholders::_1, std::placeholders::_2, layer); _eventDispatcher->addEventListenerWithSceneGraphPriority(touchListener, layer); } void ToyParentScene::repeatPickLevelPrompt(){ static int DelaySecs = 15; runAction(cocos2d::Sequence::create(cocos2d::DelayTime::create(DelaySecs), cocos2d::CallFunc::create([&](){ if(isShowingLevelPicker){ ToySoundsRepo::playSound(ToySoundsRepo::pickLevelSound().filePath); repeatPickLevelPrompt(); } }), nullptr)); }