// // SubGameSceneTapNCatch.cpp // SteveAndMaggieGame-mobile // // Created by Katarzyna Kalinowska-Górska on 07/05/2019. // #include "SubGameSceneShoot.h" #include "MathUtils.h" #include "SoundUtils.h" #include "ScalingUtils.h" #include "MiscUtils.h" #include "SoundsRepo.h" #include "PlainLabel.h" #include "SimpleButton.h" #include "GeometryUtils.h" //#include SubGameSceneShoot* SubGameSceneShoot::create(int pGameId, std::string layoutFilePath, std::function gameConfigParser){ SubGameSceneShoot * scene = new (std::nothrow) SubGameSceneShoot(); if(scene && scene->initWithConfiguration(pGameId, layoutFilePath, gameConfigParser)) { scene->autorelease(); return scene; } CC_SAFE_DELETE(scene); return nullptr; } bool SubGameSceneShoot::initWithConfiguration(int pGameId,std::string layoutFilePath, std::function gameConfigParse){ if(!SubGameScene::initWithConfiguration(layoutFilePath)){ return false; } gameId = pGameId; gameConfigParse(gameConfig); _baseSpringOmegaZero = 5;//15; _baseSpringDXForOmegaZero = 70.0; slingAmpForBaseItemV = 100; baseItemV = 200; _springDampCoeff = 0; slingHeight = 1; currentSteveHead = "steveHeadHappy"; //TODO var baseSteveHead clearGameState(); return true; } void SubGameSceneShoot::clearGameState(){ tut_intro_stop(); gameState->playState = SubGameScene::PlayState::INIT; gameState->currentLevel = (int)MiscUtils::lastLevel(); stopAllActions(); if(_layoutLoaded){ tut_hideFinger(); cancelSlingDrag(false); cancelItemDrag(); lifeIndicatorView->reset(); setStartTimeAndLabel(); emptyTrolley(); cleanupSplashedItems(); switchSteveHead("steveHeadHappy"); maggieCloseBeak(); } shootGameState.currentItem = -1; shootGameState.itemIdsOrder.clear(); auto maxId = 8; for(int i = 1; i <= maxId; ++i){ shootGameState.itemIdsOrder.push_back(i); } MathUtils::shuffleArray(shootGameState.itemIdsOrder); _currentItem = nullptr; currentlyDraggedObjectId = -1; _isDraggingSling = false; shootGameState.itemState = NONE; shootGameState.itemFirstShot = false; } void SubGameSceneShoot::onEnter(){ SubGameScene::onEnter(); disableButton("replayButton"); //TODO all this below make an init game function or sthng _slingStick = dynamic_cast(_objects["slingStick"]); _movingSling = dynamic_cast(_objects["sling"]); _slingHand = dynamic_cast(_objects["slingHandWhole"]); _slingSubHand = dynamic_cast(_objects["slingHand"]); _slingGumLeft = dynamic_cast(_objects["slingGumLeft"]); _slingGumRight = dynamic_cast(_objects["slingGumRight"]); _cart = dynamic_cast(_objects["cartWhole"]); _shelf = dynamic_cast(_objects["shelfWhole"]); _cartFoodLayer = dynamic_cast(_objects["cartFood"]); if(ScalingUtils::getDeviceAspectRatio() > 2.1f){ auto wellDoneLabel = dynamic_cast(_objects["wellDoneLabel"]); wellDoneLabel->setPositionY(cocos2d::Director::getInstance()->getVisibleSize().height-70*ScalingUtils::getAdjustedContentScaleFactor()); } _timeLabel = dynamic_cast(_objects["clockLabel"]); _timeLabel->setWidth(_timeLabel->getContentSize().width); //to ake it invariable addTouchHandler("spacinTB", prepareTouchBeganHandler(), TOUCHES_BEGAN); addTouchHandler("spacinTM", prepareTouchMovedHandler(), TOUCHES_MOVED); addTouchHandler("spacinTE", prepareTouchEndedHandler(), TOUCHES_ENDED); _movingSlingOriginalPosition = _movingSling->getParent()->convertToWorldSpace(_movingSling->getPosition()); _handOriginalPosition = _slingHand->getPosition(); _slingLeftAttachmentPoint = _slingGumLeft->getParent()->convertToWorldSpace(cocos2d::Vec2(_slingGumLeft->getBoundingBox().getMaxX(), _slingGumLeft->getBoundingBox().getMaxY())); _slingLeftStartLength = _slingGumLeft->getBoundingBox().size.height; _slingRightAttachmentPoint = _slingGumRight->getParent()->convertToWorldSpace(cocos2d::Vec2(_slingGumRight->getBoundingBox().getMinX(), _slingGumRight->getBoundingBox().getMaxY())); _slingRightStartLength = _slingGumRight->getBoundingBox().size.height; _currentItem = nullptr; shootGameState.itemState = NONE; gumsFollowTheSling(false); // auto trolleyRect = shootGameState.cartFlippedX ? GeometryUtils::flippedXRect(_cart, gameConfig.steveRect) : gameConfig.steveRect; // auto cartArea = trolleyRect;////GeometryUtils::getBoundingBoxToWorldForSubrect(_cart, trolleyRect); //// // auto texture = cocos2d::Director::getInstance()->getTextureCache()->addImage("graphics/shoot_game/graphics/wooden_shelf.png"); // auto shelfBg = cocos2d::Sprite::createWithTexture(texture); // cocos2d::Texture2D::TexParams params; // params.sAddressMode = cocos2d::backend::SamplerAddressMode::REPEAT; //GL_REPEAT; // params.tAddressMode = cocos2d::backend::SamplerAddressMode::REPEAT; // shelfBg->getTexture()->setTexParameters(params); // _cart->addChild(shelfBg); // shelfBg->setPosition(cocos2d::Vec2(cartArea.getMidX(), cartArea.getMidY())); // shelfBg->setContentSize(cartArea.size); _timeLabel->setString(MiscUtils::clockMinSecTimeString(gameConfig.levelConfigs[(int)MiscUtils::lastLevel()].levelTime)); switchSteveHead("steveHeadHappy"); prepareCartPosition(); shootGameState.isAnimatingCart = false; showTOSAcceptPopup([&](){ isShowingLevelPicker = true; showLevelPickerLayer([&]{ if(gameState->playState == SubGameScene::PlayState::INIT){ //to prevent quick double click which would make the game start twice with intro tut isShowingLevelPicker = false; stopAllActions(); SoundsRepo::stopAllSounds(); startGame(); } }, [&](int level){ setStartTimeAndLabel(); }); // auto introSoundInfo = SoundsRepo::introSound(); auto pickLevelSoundInfo = SoundsRepo::pickLevelSound(); runAction(cocos2d::Sequence::create(/*cocos2d::CallFunc::create(std::bind([&](std::string introSoundFilePath){ SoundsRepo::playSound(introSoundFilePath); },introSoundInfo.filePath)), cocos2d::DelayTime::create(introSoundInfo.soundDuration) ,*/cocos2d::CallFunc::create(std::bind([&](std::string pickLevelSoundPath){ SoundsRepo::playSound(pickLevelSoundPath); repeatPickLevelPrompt(); },pickLevelSoundInfo.filePath)), nullptr)); }); } void SubGameSceneShoot::prepareCartPosition(){ auto contentSize = getContentSize(); _cart->setPosition(cocos2d::Point(contentSize.width/2, contentSize.height*0.75f)); // _cart->setPositionX(- _cart->getBoundingBox().size.width/2); shootGameState.cartFlippedX = false; } void SubGameSceneShoot::prepareCartPositionOutside(){ _cart->stopAllActions(); shootGameState.isAnimatingCart = false; _cart->setPositionX(- _cart->getBoundingBox().size.width/2); shootGameState.cartFlippedX = false; } void SubGameSceneShoot::runCartAnimations(bool faster){ _cart->stopAllActions(); shootGameState.isAnimatingCart = true; auto winSize = cocos2d::Director::getInstance()->getWinSize(); auto x = _cart->getPositionX() > winSize.width ? -_cart->getBoundingBox().size.width/2 : winSize.width + _cart->getBoundingBox().size.width/2; auto isAdult = MiscUtils::lastLevel() == MiscUtils::Level::ADULT; shootGameState.cartFlippedX = x < 0; float deltaX = 0; if(isAdult){ auto minDeltaX = winSize.width/3; if(_cart->getPositionX() >= winSize.width/2){ x = MathUtils::getRandom(_cart->getBoundingBox().size.width/2, _cart->getPositionX()-minDeltaX); shootGameState.cartFlippedX = true; } else { x = MathUtils::getRandom(_cart->getPositionX()+minDeltaX, winSize.width-_cart->getBoundingBox().size.width/2); shootGameState.cartFlippedX = false; } deltaX = x - _cart->getPositionX(); } if(!isAdult){ flipCart(); } //////////////////////// // if(x < 0){ // auto trolleyRect = shootGameState.cartFlippedX ? GeometryUtils::flippedXRect(_cart, gameConfig.trolleyRect) : gameConfig.trolleyRect; // // // auto texture = cocos2d::Director::getInstance()->getTextureCache()->addImage("graphics/shoot_game/graphics/wooden_shelf.png"); // auto shelfBg = cocos2d::Sprite::createWithTexture(texture); // cocos2d::Texture2D::TexParams params; // params.sAddressMode = cocos2d::backend::SamplerAddressMode::REPEAT; //GL_REPEAT; // params.tAddressMode = cocos2d::backend::SamplerAddressMode::REPEAT; // shelfBg->getTexture()->setTexParameters(params); // _cart->addChild(shelfBg); // shelfBg->setPosition(cocos2d::Vec2(trolleyRect.getMidX(), trolleyRect.getMidY())); // shelfBg->setContentSize(trolleyRect.size); // } ///////////////////////// auto y = MathUtils::getRandom(_cart->getBoundingBox().size.height/2 + _shelf->getBoundingBox().getMaxY(), winSize.height-_cart->getBoundingBox().size.height/2); auto nextPoint = cocos2d::Point(x, y); auto delay = faster ? 0 : MathUtils::getRandom(0, 2); auto cartTime = gameConfig.levelConfigs[(int)MiscUtils::lastLevel()].trolleyTime; if(faster){ cartTime /= 2; } if(isAdult){ cartTime *= MAX((abs(deltaX))/winSize.width, 0.1f); delay = MathUtils::getRandom(0.15, 0.3f); } // auto cartAnimationAction = cocos2d::Sequence::create(cocos2d::DelayTime::create(delay), cocos2d::MoveTo::create(cartTime, nextPoint), cocos2d::CallFunc::create([&](){ // runCartAnimations(); // }), nullptr); auto cartAnimationAction = isAdult ? cocos2d::Sequence::create(cocos2d::DelayTime::create(delay), cocos2d::CallFunc::create([&](){ flipCart();}), cocos2d::EaseInOut::create(cocos2d::MoveBy::create(cartTime, nextPoint - _cart->getPosition()), 1.2), cocos2d::CallFunc::create([&](){ runCartAnimations(); }), nullptr) : cocos2d::Sequence::create(cocos2d::DelayTime::create(delay), cocos2d::EaseInOut::create(cocos2d::MoveTo::create(cartTime, nextPoint), 1.2), cocos2d::CallFunc::create([&](){ runCartAnimations(); }), nullptr); _cart->runAction(cartAnimationAction); //cocos2d::Spawn::create(cartYAnimationAction, // cartAnimationAction, // nullptr)); } void SubGameSceneShoot::flipCart(){ for(int i = 0; i < _cart->getChildren().size(); ++i){ auto childSprite = dynamic_cast(_cart->getChildren().at(i)); if(childSprite != nullptr){ auto oldFlipped = childSprite->isFlippedX(); childSprite->setFlippedX(shootGameState.cartFlippedX); if(childSprite->isFlippedX() != oldFlipped){ GeometryUtils::flipChildX(_cart, childSprite); } } } //TODO deduplicate code for(int i = 0; i < _cartFoodLayer->getChildren().size(); ++i){ auto childSprite = dynamic_cast(_cartFoodLayer->getChildren().at(i)); auto oldFlipped = childSprite->isFlippedX(); childSprite->setFlippedX(shootGameState.cartFlippedX); if(childSprite->isFlippedX() != oldFlipped){ GeometryUtils::flipChildX(_cart, childSprite); } } } TouchHandlerFunction SubGameSceneShoot::prepareTouchBeganHandler() { return [&](cocos2d::Touch* touch, cocos2d::Event* event){ if(gameState->playState == PlayState::PLAYING) { if (shootGameState.itemState != ItemState::SHOT && shootGameState.itemState != ItemState::SHOT_RELEASED) { auto slingGrabArea = GeometryUtils::getBoundingBoxToWorld(_slingHand, false); if (slingGrabArea.containsPoint(touch->getLocation())) { _slingDragStartLocation = touch->getLocation(); _slingOriginalDeltaAnchorPoint = _slingHand->getPosition() - _slingDragStartLocation; // just to make sure the sling's not moving at that point cancelSlingDrag(false); // to cancel the after-shot returning of the sling, actually _isDraggingSling = true; tut_showSlingDragAlongX(); return true; } } if (shootGameState.itemState == ItemState::WAITING_TO_BE_LOADED && shootGameState.currentItem >= 0) { auto shelfGrabArea = GeometryUtils::getBoundingBoxToWorld(_shelf); if (shelfGrabArea.containsPoint(touch->getLocation())) { for (auto it = gameConfig.shelfItems.begin(); it != gameConfig.shelfItems.end(); ++it) { auto groupNode = _objects[it->second.groupPictureNodeName]; auto itemGrabArea = GeometryUtils::getBoundingBoxToWorld(groupNode); if (itemGrabArea.containsPoint(touch->getLocation())) { _currentItem = cocos2d::Sprite::create( MathUtils::getRandomElement(it->second.picFilePaths)); _currentItem->setAnchorPoint(cocos2d::Vec2(0.5, 0)); addChild(_currentItem); auto paddingY = _currentItem->getBoundingBox().size.height < _shelf->getBoundingBox().size.height/2 ? _currentItem->getBoundingBox().size.height*0.6 : 0; //TODO dedup _currentItem->setPosition(touch->getLocation().x, touch->getLocation().y + paddingY); currentlyDraggedObjectId = it->first; shootGameState.itemState = ItemState::DRAGGING; break; } } } } } return false; }; } TouchHandlerFunction SubGameSceneShoot::prepareTouchMovedHandler() { return [&](cocos2d::Touch* touch, cocos2d::Event* event){ if(gameState->playState == PlayState::PLAYING) { if(_currentItem != nullptr && shootGameState.itemState == ItemState::DRAGGING){ // _currentItem->setPosition(touch->getLocation()); auto paddingY = _currentItem->getBoundingBox().size.height < _shelf->getBoundingBox().size.height/2 ? _currentItem->getBoundingBox().size.height*0.6 : 0; _currentItem->setPosition(touch->getLocation().x, touch->getLocation().y + paddingY); //TODO dedup } else if(_isDraggingSling){ auto touchLocation = touch->getLocation(); auto newMovingSlingMaxY = (touchLocation + _slingOriginalDeltaAnchorPoint).y + _slingHand->getBoundingBox().size.height/2; if(newMovingSlingMaxY > _slingStick->getBoundingBox().getMaxY()){ cancelSlingDrag(); return true; } _slingHand->setPosition(touchLocation + _slingOriginalDeltaAnchorPoint); auto leftAngleDegrees = _slingGumLeft->getRotation(); //leftAngle*180.0/3.14; auto rightAngleDegrees = _slingGumRight->getRotation(); if(abs(rightAngleDegrees) > 89 || abs(leftAngleDegrees) > 89){ cancelSlingDrag(); } return true; } } return false; }; } TouchHandlerFunction SubGameSceneShoot::prepareTouchEndedHandler(){ return [&](cocos2d::Touch* touch, cocos2d::Event* event){ if(gameState->playState != PlayState::PLAYING) { return; } if(shootGameState.itemState == DRAGGING && _currentItem != nullptr){ // _currentItem->setPosition(touch->getLocation()); auto _slingArea = GeometryUtils::getBoundingBoxToWorld(_slingStick); if(_slingArea.intersectsRect(_currentItem->getBoundingBox())){ if(currentlyDraggedObjectId == shootGameState.itemIdsOrder[shootGameState.currentItem]){ auto currentSoundPath = shootGameState.itemFirstShot ? gameConfig.shelfItems[shootGameState.itemIdsOrder[shootGameState.currentItem]].soundConf : SoundsRepo::hooraySound().filePath; SoundsRepo::playSound(currentSoundPath); placeItemInSling(_currentItem); } else { auto currentSoundPath = gameConfig.shelfItems[shootGameState.itemIdsOrder[shootGameState.currentItem]].soundNo; SoundsRepo::playSound(currentSoundPath); cancelItemDrag(); if(MiscUtils::lastLevel() != MiscUtils::Level::CHILD) { loseLife(); } } } else { cancelItemDrag(); } } else if(_isDraggingSling) { if(shootGameState.itemState == LOADED){ tryShoot(); } else { cancelSlingDrag(); } } }; } bool SubGameSceneShoot::touchHandlerForWidget(std::string objectName, cocos2d::ui::Widget::TouchEventType touchEventType){ if(objectName == "settingsButton" && touchEventType == cocos2d::ui::Widget::TouchEventType::ENDED){ if(!gameState->settingsMenuShown){ pauseGame(); showSettingsMenu(); } else { if(settingsMenu->isShowingParentalGate()){ settingsMenu->hideParentalGate(); } else { settingsMenu->hideParentalGate(); // in case showing parental gate is in progress... resumeGame(); hideSettingsMenu(); } } } return SubGameScene::touchHandlerForWidget(objectName, touchEventType); } void SubGameSceneShoot::tryShoot() { auto slingReturnPoint = (_slingLeftAttachmentPoint + _slingRightAttachmentPoint)/2; auto movingSlingWorldPos = _movingSling->getParent()->convertToWorldSpace(_movingSling->getPosition()); _slingDragTotalDelta = movingSlingWorldPos - slingReturnPoint; // __android_log_print(ANDROID_LOG_DEBUG, "LOG_TAG", "Need to print : %f",_slingDragTotalDelta.length()); static float minDelta = _movingSling->getBoundingBox().size.width; if( _slingDragTotalDelta.length() < minDelta){ cancelSlingDrag(); tut_showSlingDrag(); } else { shoot(); tut_hideFinger(); } } void SubGameSceneShoot::shoot() { //TODO throw item action should be in between the scale to and rotate to action //add bounce if(_currentItem != nullptr){ _isDraggingSling = false; shootGameState.itemFirstShot = false; SoundsRepo::playSound(SoundsRepo::SHOOT_GAME_SOUND_EFFECT_CATAPULT, false); auto itemBBToWorld = GeometryUtils::getBoundingBoxToWorld(_currentItem, false); _currentItem->retain(); _currentItem->removeFromParent(); addChild(_currentItem); _currentItem->release(); _currentItem->setPosition(itemBBToWorld.getMidX(), itemBBToWorld.getMidY()); // _currentItem->setRotation(_slingGumLeft->getRotation()); auto slingReturnPoint = (_slingLeftAttachmentPoint + _slingRightAttachmentPoint)/2; auto movingSlingWorldPos = _movingSling->getParent()->convertToWorldSpace(_movingSling->getPosition()); moveSlingToTheWorld(); _slingDragTotalDelta = movingSlingWorldPos - slingReturnPoint;//_slingDragStartLocation; _slingAmplitude = _slingDragTotalDelta.length(); //!!!!! TODO LENGTH NIEKONIECZNE ZNACZY TO CO MYSLISZ!!!!!! _slingGumLeftShootRotation = _slingGumLeft->getRotation(); _slingGumRightShootRotation = _slingGumRight->getRotation(); _oscMidPoint = movingSlingWorldPos - cocos2d::Vec2(_slingDragTotalDelta.x, _slingDragTotalDelta.y); _springOmegaZero = _baseSpringOmegaZero*_slingAmplitude/_baseSpringDXForOmegaZero; auto eps = 3.14/2.5; _slingReturnAnimTotalTime = ((3.0*3.14/2)+eps)/_springOmegaZero; auto alpha = _springOmegaZero*_slingReturnAnimTotalTime - 3.14; _springDampCoeff = -_springOmegaZero*sin(alpha)/cos(alpha); // printf("TOTAL TIME: %f\n damp coeff: %f dx: %f\n", _slingReturnAnimTotalTime, _springDampCoeff, _slingAmplitude); _totalReturningTime = 0; _isSlingReturning = true; prevCosVal = -5; shootGameState.itemState = ItemState::SHOT; } else { cancelSlingDrag(); } } void SubGameSceneShoot::cancelSlingDrag(bool onlyOnStateDragging){ if(_isDraggingSling || !onlyOnStateDragging){ _movingSling->stopAllActions(); _slingHand->stopAllActions(); _isDraggingSling = false; _isSlingReturning = false; //all of this should have been resolved better - the sling hsould have its own state var / enum, just like the item _slingGumRight->setRotation(0); _slingGumLeft->setRotation(0); _slingGumRight->setScaleY(1); _slingGumLeft->setScaleY(1); _slingHand->setPosition(_handOriginalPosition); _movingSling->setPosition(_movingSlingOriginalPosition); moveSlingBackToTheHand(); if(shootGameState.itemState == ItemState::LOADED){ tut_showSlingDrag(); } else { tut_showItemDrag(); } } } void SubGameSceneShoot::pauseGame(){ SubGameScene::pauseGame(); unscheduleUpdate(); SoundsRepo::pauseAllSounds(); pause(); _cart->pause(); if(hintFingerSprite != nullptr){ hintFingerSprite->pause(); } } void SubGameSceneShoot::resumeGame(){ SubGameScene::resumeGame(); if(gameState->playState == SubGameScene::PlayState::PLAYING && shootGameState.itemState != ItemState::IN_TROLLEY) { auto currentSoundPath = currentItem().soundRequest; SoundsRepo::playSound(currentSoundPath); } scheduleUpdate(); resume(); SoundsRepo::resumeAllSounds(); _cart->resume(); if(hintFingerSprite != nullptr){ hintFingerSprite->resume(); } } void SubGameSceneShoot::itemFollowsTheSling() { if(_currentItem != nullptr) { _currentItem->setPosition(_movingSling->getBoundingBox().getMidX(), _movingSling->getBoundingBox().getMaxY()); } } void SubGameSceneShoot::gumsFollowTheSling(bool switchRotation){ static auto leftDX = 6.f; static auto leftDY = -20.f; static auto rightDX = -6.f; static auto rightDY = -20.f; auto leftGumDragPoint = cocos2d::Point(_movingSling->getParent()->convertToWorldSpace(cocos2d::Vec2(_movingSling->getBoundingBox().getMinX() + leftDX, _movingSling->getBoundingBox().getMaxY() + leftDY))); auto newLeftSlingVec = leftGumDragPoint - _slingLeftAttachmentPoint; _slingGumLeft->setScaleY(newLeftSlingVec.getLength()/_slingLeftStartLength); //!!!!! TODO LENGTH NIEKONIECZNE ZNACZY TO CO MYSLISZ!!!!!! auto rightGumDragPoint = cocos2d::Point(_movingSling->getParent()->convertToWorldSpace(cocos2d::Vec2(_movingSling->getBoundingBox().getMaxX() + rightDX, _movingSling->getBoundingBox().getMaxY()+rightDY))); auto newRightSlingVec = rightGumDragPoint - _slingRightAttachmentPoint; _slingGumRight->setScaleY(newRightSlingVec.getLength()/_slingRightStartLength); //!!!!! TODO LENGTH NIEKONIECZNE ZNACZY TO CO MYSLISZ!!!!!! if(switchRotation){ _slingGumLeft->setRotation(_slingGumLeftShootRotation + 180); _slingGumRight->setRotation(_slingGumRightShootRotation + 180); } else { auto leftAngle = newLeftSlingVec.x == 0 || newLeftSlingVec.y == 0 ? 0 : atan((newLeftSlingVec.x)/(newLeftSlingVec.y)); auto leftAngleDegrees = leftAngle*180.0/3.14; _slingGumLeft->setRotation(leftAngleDegrees); auto rightAngle = newRightSlingVec.x == 0 || newRightSlingVec.y == 0 ? 0 : atan((newRightSlingVec.x)/(newRightSlingVec.y)); auto rightAngleDegrees = rightAngle*180.0/3.14; _slingGumRight->setRotation(rightAngleDegrees); } } //static int g = 0; //use to emulate an extreely slow device void SubGameSceneShoot::update(float dt){ //use to emulate an extreely slow device // if((g++)%10 != 0) return; // dt = dt*10; ////////////////////////////////////////////////////////////////////////////////////////// SubGameScene::update(dt); gumsFollowTheSling(false); //gums should always follow the sling if(_isSlingReturning){ _totalReturningTime += dt; auto cosVal = cos(_springOmegaZero*_totalReturningTime - 3.14); auto slingDD = -_slingAmplitude*exp(-_springDampCoeff*_totalReturningTime)*cosVal;// - _slingAmplitude; // auto prevPos = _movingSling->getPosition(); auto slingDX = slingDD*_slingDragTotalDelta.x/_slingAmplitude; auto slingDY = slingDD*_slingDragTotalDelta.y/_slingAmplitude; _movingSling->setPosition(_oscMidPoint.x + slingDX, _oscMidPoint.y + slingDY); if(shootGameState.itemState == ItemState::SHOT){ if(cosVal > prevCosVal){ itemFollowsTheSling(); } else {//if(shootGameState.itemState != ItemState::SHOT_RELEASED){ itemFlyTime = sqrt(2*slingHeight/9.81f); itemFlyTimeElapsed = 0; itemV = baseItemV*_slingAmplitude/slingAmpForBaseItemV; shootGameState.itemState = ItemState::SHOT_RELEASED; } } prevCosVal = cosVal; gumsFollowTheSling(cosVal > 0); //fix the gums auto eps = 0.01; if(_totalReturningTime >= _slingReturnAnimTotalTime){ _isSlingReturning = false; auto animTime = 0.3f; _movingSling->runAction(cocos2d::Sequence::create(cocos2d::MoveTo::create(animTime, _movingSlingOriginalPosition), nullptr)); _slingHand->runAction(cocos2d::Sequence::create(cocos2d::MoveTo::create(animTime, _handOriginalPosition), cocos2d::CallFunc::create([&]{ moveSlingBackToTheHand(); }), nullptr)); if(shootGameState.itemState == ItemState::SHOT){ //it should always be SHOT_RELEASED at this point. It's just a fix for VERY slow devices (emulators...), for which if(cosVal > prevCosVal) always evaluates to true, and then _isSlingReturning is being called before the item can be shot out itemFlyTime = sqrt(2*slingHeight/9.81f); itemFlyTimeElapsed = 0; itemV = baseItemV*_slingAmplitude/slingAmpForBaseItemV; shootGameState.itemState = ItemState::SHOT_RELEASED; //TODO dedup } } } if(shootGameState.itemState == ItemState::SHOT_RELEASED && _currentItem != nullptr) { itemFlyTimeElapsed += dt; auto itemDX = -itemV*dt*_slingDragTotalDelta.x/_slingAmplitude; auto itemDY = -itemV*dt*_slingDragTotalDelta.y/_slingAmplitude; _currentItem->setPosition(_currentItem->getPositionX()+itemDX,_currentItem->getPositionY()+itemDY); auto h = cocos2d::Director::getInstance()->getWinSize().height/2; _currentItem->setScale(MAX(0.5f,(h-itemV*itemFlyTimeElapsed)/h));//todo to musi byc miara odleglosci przebytej przez stala odleglosc //TODO parametrize 0.5 auto trolleyRect = shootGameState.cartFlippedX ? GeometryUtils::flippedXRect(_cart, gameConfig.trolleyRect) : gameConfig.trolleyRect; auto cartArea = GeometryUtils::getBoundingBoxToWorldForSubrect(_cart, trolleyRect); static auto splashTime = 2; if(cartArea.intersectsRect(_currentItem->getBoundingBox())){ shootGameState.itemState = ItemState::IN_TROLLEY; _currentItem->retain(); _currentItem->removeFromParent(); _cartFoodLayer->addChild(_currentItem); _currentItem->setScale(0.5); _currentItem->release(); auto position = gameConfig.trolleyItemPoints[shootGameState.currentItem]; if(shootGameState.cartFlippedX){ position = GeometryUtils::flippedXPoint(_cart, position); _currentItem->setFlippedX(true); } position.y += _currentItem->getBoundingBox().size.height/2; _currentItem->setPosition(position); if(shootGameState.currentItem >= gameConfig.trolleyItemPoints.size()/2){ _currentItem->setLocalZOrder(1); } else { _currentItem->setLocalZOrder(2); } _currentItem = nullptr; if(gameState->playState != PlayState::PRE_PLAYING){ SoundsRepo::playSound(SoundsRepo::SHOOT_GAME_SOUND_IN_TROLLEY); SoundsRepo::playSound(SoundsRepo::SHOOT_GAME_SOUND_EFFECT_IN_TROLLEY, false); tut_hideFinger(true); saveTutorialComplete(); runAction(cocos2d::Sequence::create(cocos2d::DelayTime::create(5), cocos2d::CallFunc::create([&]{ nextItem(); }), NULL)); } else { // == if playing the tutorial if(shootGameState.currentTutorialItem == 1){ scheduleOnce([&](float){ tut_intro_shootChocolate(); }, 0.4f, "ShootChoc"); } else if(shootGameState.currentTutorialItem == 2){ scheduleOnce([&](float){ tut_intro_shootBanana(); }, 0.4f, "ShootBanana"); } ++shootGameState.currentTutorialItem; } } else { auto steveRect = shootGameState.cartFlippedX ? GeometryUtils::flippedXRect(_cart, gameConfig.steveRect) : gameConfig.steveRect; auto steveArea = GeometryUtils::getBoundingBoxToWorldForSubrect(_cart, steveRect); if (steveArea.intersectsRect(_currentItem->getBoundingBox())) { SoundsRepo::playSound(SoundsRepo::SHOOT_GAME_SOUND_HIT_STEVE); shootGameState.itemState = ItemState::WAITING_TO_BE_LOADED; tut_showItemDrag(); SoundsRepo::playSound(SoundsRepo::SHOOT_GAME_SOUND_EFFECT_SPLAT, false); auto item = splashItem(_cart, cocos2d::Point(steveRect.getMidX(), steveRect.getMidY()-steveRect.size.height/4), splashTime); item->setFlippedX(shootGameState.cartFlippedX); switchSteveHead("steveHeadGrr"); runAction(cocos2d::Sequence::create(cocos2d::DelayTime::create(splashTime), cocos2d::CallFunc::create([&](){ switchSteveHead("steveHeadHappy"); }), NULL)); } else { auto maggieRect = shootGameState.cartFlippedX ? GeometryUtils::flippedXRect(_cart, gameConfig.maggieRect) : gameConfig.maggieRect; auto maggieArea = GeometryUtils::getBoundingBoxToWorldForSubrect(_cart, maggieRect); if (maggieArea.intersectsRect(_currentItem->getBoundingBox())) { shootGameState.itemState = ItemState::WAITING_TO_BE_LOADED; tut_showItemDrag(); SoundsRepo::playSound(SoundsRepo::SHOOT_GAME_SOUND_HIT_MAGGIE); SoundsRepo::playSound(SoundsRepo::SHOOT_GAME_SOUND_EFFECT_SPLAT , false); auto item = splashItem(_cart, cocos2d::Point(maggieRect.getMidX(), gameConfig.maggieRect.getMidY()-maggieRect.size.height/4), 2); item->setFlippedX(shootGameState.cartFlippedX); maggieOpenBeak(); runAction(cocos2d::Sequence::create(cocos2d::DelayTime::create(splashTime), cocos2d::CallFunc::create([&](){ maggieCloseBeak(); }), NULL)); } else if (itemFlyTimeElapsed >= itemFlyTime && !_cart->getBoundingBox().intersectsRect(_currentItem->getBoundingBox())){ SoundsRepo::playSound(SoundsRepo::SHOOT_GAME_SOUND_NOT_IN_TROLLEY); SoundsRepo::playSound(SoundsRepo::SHOOT_GAME_SOUND_EFFECT_SPLAT , false); shootGameState.itemState = ItemState::WAITING_TO_BE_LOADED; tut_showItemDrag(); splashItem(_cart->getParent(), _currentItem->getPosition(), 1, _cart->getLocalZOrder()-1); auto random = MathUtils::getRandomInt(0, 1); switchSteveHead(random > 0 ? "steveHeadOhNo1" : "steveHeadOhNo2"); runAction(cocos2d::Sequence::create(cocos2d::DelayTime::create(splashTime), cocos2d::CallFunc::create([&](){ switchSteveHead("steveHeadHappy"); }), NULL)); } } } } // if(shootGameState.isAnimatingCart && shootGameState.cartSineMovement){ // auto dx = dt*shootGameState.cartBaseVelocity*shootGameState.cartVelocityModifier; // _cart->setPositionX(_cart->getPositionX()+dx); // auto dy = _cart->getBoundingBox().size.height*shootGameState.cartSineAmpModifier*sin(_cart->getPositionX()/shootGameState.cartSineBreadth + shootGameState.cartSineShift); // _cart->setPositionY(MAX(shootGameState.cartBaseY + dy, _cart->getBoundingBox().size.height/2*1.3)); // } } void SubGameSceneShoot::maggieOpenBeak(){ auto maggieOpenBeak = _objects["cartBack2"]; auto maggieClosedBeak = _objects["cartBack"]; maggieOpenBeak->setOpacity(255); maggieClosedBeak->setOpacity(0); } void SubGameSceneShoot::maggieCloseBeak(){ auto maggieOpenBeak = _objects["cartBack2"]; auto maggieClosedBeak = _objects["cartBack"]; maggieOpenBeak->setOpacity(0); maggieClosedBeak->setOpacity(255); } void SubGameSceneShoot::switchSteveHead(std::string headName){ if(headName != currentSteveHead) { auto steveCurrentFace = _objects[currentSteveHead]; if (steveCurrentFace != nullptr) { steveCurrentFace->setOpacity(0); } auto steveNewFace = _objects[headName]; if (steveNewFace != nullptr) { steveNewFace->setOpacity(255); } currentSteveHead = headName; } } void SubGameSceneShoot::placeItemInSling(cocos2d::Sprite* _item){ shootGameState.itemState = ItemState::LOADED; _item->setAnchorPoint(cocos2d::Vec2(0.5, 0.5)); _item->retain(); _item->removeFromParent(); _slingHand->addChild(_item); _item->release(); _item->setPosition(_movingSling->getBoundingBox().getMidX(), _movingSling->getBoundingBox().getMaxY()); auto slingComponents = _slingHand->getChildren(); for(int i = 0; i < slingComponents.size(); ++i){ if(slingComponents.at(i) != _item){ _slingHand->reorderChild(slingComponents.at(i), 10); } } tut_showSlingDrag(); } void SubGameSceneShoot::removeItemFromSling(){ if(_currentItem != nullptr && shootGameState.itemState == ItemState::LOADED) { _currentItem->removeFromParent(); _currentItem = nullptr; } } void SubGameSceneShoot::cancelItemDrag(){ shootGameState.itemState = ItemState::WAITING_TO_BE_LOADED; if(_currentItem != nullptr){ _currentItem->removeFromParent(); _currentItem = nullptr; currentlyDraggedObjectId = -1; } } void SubGameSceneShoot::moveSlingToTheWorld(){ auto movingSlingWorldPos = _movingSling->getParent()->convertToWorldSpace(_movingSling->getPosition()); _movingSling->retain(); _movingSling->removeFromParent(); addChild(_movingSling); _movingSling->setPosition(movingSlingWorldPos); _movingSling->release(); } void SubGameSceneShoot::moveSlingBackToTheHand(){ auto movingSlingPos = _slingHand->convertToNodeSpace(_movingSling->getPosition()); _movingSling->retain(); _movingSling->removeFromParent(); _slingHand->addChild(_movingSling); _movingSling->setPosition(movingSlingPos); _movingSling->release(); _movingSling->setLocalZOrder(1); _slingSubHand->setLocalZOrder(2); if(shootGameState.itemState == ItemState::LOADED) { itemFollowsTheSling(); } } void SubGameSceneShoot::scrollShelf(cocos2d::Node* nodeToBeVisible, std::function completion){ auto winWidth = cocos2d::Director::getInstance()->getWinSize().width; auto bb = GeometryUtils::getBoundingBoxToWorld(nodeToBeVisible); if (bb.getMaxX() > winWidth || bb.getMinX() < 0 || checkRectHidesBehindSling(bb)) { // if out of the screen or the item hides behind the sling, which makes it difficult to drag the item // it's not fully visible, we need to move the shelf // pick a random x so that the whole item will be visible on the screen, shift the shelf accordingly float x, dx; auto newBB = bb; int iter = 0; do { x = MathUtils::getRandom(nodeToBeVisible->getBoundingBox().size.width, winWidth-nodeToBeVisible->getBoundingBox().size.width); dx = x - bb.origin.x; newBB.origin.x = x; ++iter; } while((abs(dx) < bb.size.width*2 && iter < 20) || checkRectHidesBehindSling(newBB)); // too small delta means task too easy, the second condition prevents the situation when the item hides behind the sling, which would make it difficult to drag the item auto shelfPadding = 30; if(_shelf->getBoundingBox().getMinX() + dx > -shelfPadding){ dx = -shelfPadding - _shelf->getBoundingBox().getMinX(); } else if(_shelf->getBoundingBox().getMaxX() + dx < winWidth + shelfPadding){ dx = winWidth + shelfPadding - _shelf->getBoundingBox().getMaxX(); } // nodeToBeVisible->runAction(cocos2d::MoveBy::create(0.4, cocos2d::Vec2(dx,0))); _shelf->runAction(cocos2d::Sequence::create(cocos2d::MoveBy::create(0.4, cocos2d::Vec2(dx,0)), cocos2d::CallFunc::create(completion), nullptr)); } else { //else it's fully visible already, so leave it as it is and just call the completion completion(); } } bool SubGameSceneShoot::checkRectHidesBehindSling(cocos2d::Rect rectWorldCoord){ auto width = _slingStick->getBoundingBox().size.width; auto slingMinX = _handOriginalPosition.x - width/2; auto slingMaxX = _handOriginalPosition.x + width/2; return !(rectWorldCoord.getMaxX() <= slingMinX || rectWorldCoord.getMinX() >= slingMaxX); } void SubGameSceneShoot::nextItem(){ if(shootGameState.currentItem == shootGameState.itemIdsOrder.size() - 1){ gameWon(); stopAllActions(); unscheduleAllCallbacks(); SoundsRepo::playSound(SoundsRepo::SHOOT_GAME_SOUND_ALL_IN_TROLLEY); switchSteveHead("steveHeadHappy"); _cart->stopAllActions(); shootGameState.isAnimatingCart = false; _cart->runAction(cocos2d::MoveTo::create(1, cocos2d::Vec2(getBoundingBox().size.width/2, getBoundingBox().size.height*2/3.0))); showWellDoneAndReplay(); } else { scheduleUpdate(); shootGameState.itemFirstShot = true; shootGameState.wrongItemsCount = 0; lifeIndicatorView->reset(); ++shootGameState.currentItem; auto item = currentItem(); auto currentSoundPath = item.soundRequest; SoundsRepo::playSound(currentSoundPath); auto correspondingShelfNode = _objects[item.groupPictureNodeName]; scrollShelf(correspondingShelfNode, [&](){ shootGameState.itemState = ItemState::WAITING_TO_BE_LOADED; tut_showItemDrag();}); } } cocos2d::Sprite* SubGameSceneShoot::splashItem(cocos2d::Node* where, cocos2d::Vec2 pos, float forHowLong, int zOrder){ // if(MiscUtils::lastLevel() == MiscUtils::Level::ADULT) { // loseLife(); // } auto splashedItem = cocos2d::Sprite::create(currentItem().splodgePicFilePath); if(_currentItem){ _currentItem->removeFromParent(); _currentItem = nullptr; } where->addChild(splashedItem); splashedItem->setPosition(pos); splashedItem->setLocalZOrder(zOrder); shootGameState.splashedItems.push_back(splashedItem); if(forHowLong > 0){ runAction(cocos2d::Sequence::create(cocos2d::DelayTime::create(forHowLong), cocos2d::CallFunc::create(std::bind([&](cocos2d::Node* item){ shootGameState.splashedItems.erase(std::find(shootGameState.splashedItems.begin(), shootGameState.splashedItems.end(), item)); item->removeFromParent(); }, splashedItem)), nullptr)); } return splashedItem; } void SubGameSceneShoot::startGame(bool playIntro){ gameState->currentLevel = (int)MiscUtils::lastLevel(); gameState->playState = PlayState::PRE_PLAYING; lifeIndicatorView->setVisible(gameState->currentLevel != (int)MiscUtils::Level::CHILD); scheduleUpdate(); if(playIntro){ tut_intro_start(); runAction(cocos2d::Sequence::create(cocos2d::CallFunc::create([&](){ SoundsRepo::playSound(SoundsRepo::SHOOT_GAME_SOUND_INTRO); //TODO FIXED TIME BAD BAD BAD sounds.info todo }), cocos2d::DelayTime::create(10.4), cocos2d::CallFunc::create([&](){ disableFastForwardButton(); startGame(false); // gameState->playState = PlayState::PLAYING; // nextItem(); // startTimer(); }), nullptr)); } else { tut_intro_stop(); if(!shootGameState.isAnimatingCart){ auto faster = _cart->getBoundingBox().containsPoint(cocos2d::Point(getContentSize().width/2, getContentSize().height/2)); runCartAnimations(faster); } gameState->playState = PlayState::PLAYING; switchSteveHead("steveHeadHappy"); nextItem(); startTimer(); } } void SubGameSceneShoot::startTimer(){ setStartTimeAndLabel(); this->schedule(CC_SCHEDULE_SELECTOR(SubGameSceneShoot::onSecondElapsed), 1, CC_REPEAT_FOREVER, 0); } void SubGameSceneShoot::setStartTimeAndLabel(){ shootGameState.currentTimeSeconds = gameConfig.levelConfigs[(int)MiscUtils::lastLevel()].levelTime; _timeLabel->setString(MiscUtils::clockMinSecTimeString(shootGameState.currentTimeSeconds)); } void SubGameSceneShoot::onSecondElapsed(float dt){ --shootGameState.currentTimeSeconds; _timeLabel->setString(MiscUtils::clockMinSecTimeString(shootGameState.currentTimeSeconds)); if(shootGameState.currentTimeSeconds <= 0){ this->unschedule(CC_SCHEDULE_SELECTOR(SubGameSceneShoot::onSecondElapsed)); gameLost(); fadeDarkLayer(true); SoundsRepo::playSound(SoundsRepo::SHOOT_GAME_SOUND_SHOP_CLOSED); } } void SubGameSceneShoot::cleanupSplashedItems(){ for(int i = 0; i < shootGameState.splashedItems.size(); ++i){ shootGameState.splashedItems.at(i)->removeFromParent(); } shootGameState.splashedItems.clear(); } void SubGameSceneShoot::emptyTrolley(){ if(_cartFoodLayer != nullptr){ _cartFoodLayer->removeAllChildren(); } } void SubGameSceneShoot::loseLife(){ ++shootGameState.wrongItemsCount; lifeIndicatorView->loseLife(); if (shootGameState.wrongItemsCount >= gameConfig.lives) { gameLost(); SoundsRepo::playSound(SoundsRepo::SHOOT_GAME_SOUND_WRONG_THREE_TIMES); } } void SubGameSceneShoot::gameLost(){ SubGameScene::gameLost(); stopAllActions(); unscheduleAllCallbacks(); SoundsRepo::stopAllSounds(); switchSteveHead("steveHeadSad"); _cart->stopAllActions(); auto positionX = shootGameState.cartFlippedX ? -_cart->getBoundingBox().size.width/2 : _cart->getParent()->getBoundingBox().size.width + _cart->getBoundingBox().size.width/2; // _cart->runAction(cocos2d::MoveTo::create(2, cocos2d::Vec2(positionX, _cart->getPositionY()))); _cart->runAction(cocos2d::Sequence::create(cocos2d::MoveTo::create(1, cocos2d::Vec2(getBoundingBox().size.width/2, getBoundingBox().size.height*2/3.0)), cocos2d::DelayTime::create(5), cocos2d::CallFunc::create([&](){ auto positionX = shootGameState.cartFlippedX ? -_cart->getBoundingBox().size.width/2 : _cart->getParent()->getBoundingBox().size.width + _cart->getBoundingBox().size.width/2; _cart->runAction(cocos2d::MoveTo::create(2.f, cocos2d::Vec2(positionX, _cart->getPositionY()))); }), nullptr)); shootGameState.isAnimatingCart = false; emptyTrolley(); cancelItemDrag(); cancelSlingDrag(false); removeItemFromSling(); tut_hideFinger(); switchMenuToReplayButton(); } bool SubGameSceneShoot::onReplayButtonClicked() { if(gameState->playState == SubGameScene::PlayState::PRE_PLAYING){ tut_intro_stop(); } fadeDarkLayer(false); hideWellDoneAndReplay(); disableButton("fastForwardButton"); // disableButton("replayButton"); clearAllItems(); return SubGameScene::onReplayButtonClicked(); } bool SubGameSceneShoot::onFastForwardButtonClicked() { // auto anotherLabel = cocos2d::Label::createWithTTF("Another example label", "fonts/ComicSansMSBold.ttf", 120*ScalingUtils::getScaleForFont()); // addChild(anotherLabel); // cancelSlingDrag(false); removeItemFromSling(); return SubGameScene::onFastForwardButtonClicked(); } void SubGameSceneShoot::switchMenuToReplayButton(){ auto replayButton = _objects["replayButton"]; auto ffButton = _objects["fastForwardButton"]; auto pauseButton = _objects["pauseButton"]; enableButton("replayButton"); disableButton("fastForwardButton"); MiscUtils::showView(replayButton, true); MiscUtils::hideView(ffButton, true); MiscUtils::hideView(pauseButton, true); MiscUtils::hideView(lifeIndicatorView, true); } void SubGameSceneShoot::fadeDarkLayer(bool fadeIn) { if (darkLayer == nullptr) { darkLayer = cocos2d::LayerColor::create(cocos2d::Color4B(0, 0, 0, 0)); _slingStick->getParent()->addChild(darkLayer); _slingHand->setLocalZOrder(9); darkLayer->setLocalZOrder(20); } darkLayer->stopAllActions(); if(fadeIn){ darkLayer->runAction(cocos2d::FadeTo::create(2, 180)); } else { darkLayer->runAction(cocos2d::FadeOut::create(0.2)); } } void SubGameSceneShoot::showWellDoneAndReplay(){ auto replayButton = _objects["replayButton"]; auto ffButton = _objects["fastForwardButton"]; auto pauseButton = _objects["pauseButton"]; auto wellDoneBackground = _objects["wellDoneBackground"]; auto wellDoneLabel = dynamic_cast(_objects["wellDoneLabel"]); MiscUtils::showView(wellDoneBackground, true); MiscUtils::showView(wellDoneLabel, true); MiscUtils::showView(replayButton, true); enableButton("replayButton"); MiscUtils::hideView(ffButton, true); MiscUtils::hideView(pauseButton, true); MiscUtils::hideView(lifeIndicatorView, true); } void SubGameSceneShoot::hideWellDoneAndReplay(){ auto replayButton = _objects["replayButton"]; auto ffButton = _objects["fastForwardButton"]; auto pauseButton = _objects["pauseButton"]; auto wellDoneBackground = _objects["wellDoneBackground"]; auto wellDoneLabel = dynamic_cast(_objects["wellDoneLabel"]); MiscUtils::hideView(wellDoneBackground, true); MiscUtils::hideView(wellDoneLabel, true); MiscUtils::hideView(replayButton, true); MiscUtils::showView(ffButton, true, 128); MiscUtils::showView(lifeIndicatorView, true); MiscUtils::showView(pauseButton, true); enableButton("pauseButton"); disableButton("replayButton"); disableButton("fastForwardButton"); } void SubGameSceneShoot::hideSettingsMenuWithLevelReset(){ resumeGame(); hideSettingsMenu(true); auto newLevel = (int)(MiscUtils::lastLevel()); if(newLevel != gameState->currentLevel){ prepareCartPositionOutside(); onReplayButtonClicked(); } } // TUTORIAL void SubGameSceneShoot::tut_showItemDrag(){ if(gameState->playState == PlayState::PLAYING && !tutorialComplete()){ auto item = _objects[currentItem().groupPictureNodeName]; auto currentItemBB = item->getBoundingBox(); auto currentItemMidPoint = cocos2d::Point(currentItemBB.getMidX(), currentItemBB.getMidY()); currentItemMidPoint = item->getParent()->convertToWorldSpace(currentItemMidPoint); auto slingMidPoint = cocos2d::Vec2(_handOriginalPosition.x, _handOriginalPosition.y*1.5f); slingMidPoint = _slingHand->getParent()->convertToWorldSpace(slingMidPoint); tut_moveFinger(currentItemMidPoint, slingMidPoint, 2, 0.6, true); } } //todo make the finger react to the position of the trolley void SubGameSceneShoot::tut_showSlingDrag(){ if(gameState->playState == PlayState::PLAYING && !tutorialComplete()){ tut_prepareFingerIfNeeded(); auto slingHighPoint = cocos2d::Vec2(_slingHand->getBoundingBox().getMidX(), _slingHand->getBoundingBox().getMaxY()*0.8f); slingHighPoint = _slingHand->getParent()->convertToWorldSpace(slingHighPoint); auto deltaXToTrolley = _cart->getPositionX() - _handOriginalPosition.x; auto deltaX = -deltaXToTrolley/4; //TODO magic number auto deltaY = -_slingHand->getBoundingBox().size.height/4; tut_moveFinger(slingHighPoint, cocos2d::Point(slingHighPoint.x + deltaX, slingHighPoint.y + deltaY), 0.3f, 0.1f, false, [&](){ tut_showSlingDrag(); }); } } void SubGameSceneShoot::tut_showSlingDragAlongX(){ if(gameState->playState == PlayState::PLAYING && !tutorialComplete()){ tut_prepareFingerIfNeeded(); auto bottomYForFinger = hintFingerSprite->getAnchorPoint().y * hintFingerSprite->getBoundingBox().size.height/1.4f; //because rotated by 45 :P // auto deltaToTrolley = _cart->getPosition() - _movingSlingOriginalPosition; // auto multiplier = 0.2; // auto deltaX = (deltaToTrolley.x * (bottomYForFinger - _movingSlingOriginalPosition.y) / deltaToTrolley.y)*multiplier; auto deltaX = _slingHand->getBoundingBox().size.width*2; tut_moveFingerBackAndForth(cocos2d::Point(_movingSlingOriginalPosition.x - deltaX, bottomYForFinger), cocos2d::Point(_movingSlingOriginalPosition.x + deltaX, bottomYForFinger), 1, 0.1); } } void SubGameSceneShoot::tut_moveFinger(const cocos2d::Point& startPosition, const cocos2d::Point& endPosition, float moveTime, float pauseTime, bool loop, std::function completion){ if(!tutorialComplete()){ tut_prepareFingerIfNeeded(); hintFingerSprite->setPosition(startPosition); auto moveAction = cocos2d::Sequence::create(cocos2d::FadeIn::create(MiscUtils::StandardAnimationTime), cocos2d::EaseInOut::create( cocos2d::MoveTo::create(moveTime, endPosition), 1.2), cocos2d::DelayTime::create(pauseTime), cocos2d::FadeOut::create(MiscUtils::StandardAnimationTime), cocos2d::CallFunc::create([&, startPosition](){ hintFingerSprite->setPosition(startPosition); }), cocos2d::FadeIn::create(MiscUtils::StandardAnimationTime), nullptr); if(loop){ hintFingerSprite->runAction(cocos2d::RepeatForever::create(moveAction)); } else { hintFingerSprite->runAction(cocos2d::Sequence::create(moveAction, cocos2d::CallFunc::create(completion), nullptr)); } } } void SubGameSceneShoot::tut_moveFingerBackAndForth(const cocos2d::Point& startPosition, const cocos2d::Point& endPosition, float moveTime, float pauseTime){ if(!tutorialComplete()){ tut_prepareFingerIfNeeded(); hintFingerSprite->setPosition(startPosition); auto moveAction = cocos2d::Sequence::create(cocos2d::FadeIn::create(MiscUtils::StandardAnimationTime), cocos2d::EaseInOut::create( cocos2d::MoveTo::create(moveTime, endPosition), 1.2), cocos2d::DelayTime::create(pauseTime), cocos2d::EaseInOut::create( cocos2d::MoveTo::create(moveTime, startPosition), 1.2), nullptr); hintFingerSprite->runAction(cocos2d::RepeatForever::create(moveAction)); } } void SubGameSceneShoot::tut_prepareFingerIfNeeded(){ if(hintFingerSprite == nullptr) { hintFingerSprite = cocos2d::Sprite::create( "graphics/g_finger.png"); addChild(hintFingerSprite, 190); hintFingerSprite->setAnchorPoint(cocos2d::Vec2(0.4f, 14/15.f)); //TODO fixed hintFingerSprite->setRotation(-45); hintFingerSprite->setColor(cocos2d::Color3B(200, 200, 200)); } else { hintFingerSprite->stopAllActions(); hintFingerSprite->setVisible(true); } } void SubGameSceneShoot::tut_hideFinger(bool remove){ if(hintFingerSprite != nullptr){ hintFingerSprite->stopAllActions(); if(remove){ MiscUtils::hideAndRemoveView(hintFingerSprite, true); hintFingerSprite = nullptr; } else { MiscUtils::hideView(hintFingerSprite, true); } } } static int TutIntroActionTag = 10; void SubGameSceneShoot::tut_intro_start(){ shootGameState.currentTutorialItem = 1; tut_intro_shootApple(); } void SubGameSceneShoot::tut_intro_stop(){ if(_layoutLoaded) { unscheduleAllCallbacks(); stopActionByTag(TutIntroActionTag); _slingHand->stopAllActions(); _movingSling->stopAllActions(); emptyTrolley(); cancelItemDrag(); cancelSlingDrag(false); removeItemFromSling(); shootGameState.currentItem = -1; shootGameState.itemState = ItemState::NONE; } } void SubGameSceneShoot::tut_intro_shootApple(){ shootGameState.currentItem = 0; tut_intro_shootItem("apples", "graphics/shoot_game/graphics/single_food/apple1.png"); } void SubGameSceneShoot::tut_intro_shootChocolate(){ shootGameState.currentItem = 1; tut_intro_shootItem("chocolate", "graphics/shoot_game/graphics/single_food/chocolate1.png"); } void SubGameSceneShoot::tut_intro_shootBanana(){ shootGameState.currentItem = 1; tut_intro_shootItem("bananas", "graphics/shoot_game/graphics/single_food/banana1.png", 0.6); } void SubGameSceneShoot::tut_intro_shootItem(std::string groupNodeName, std::string singleItemFilePath, float moveAnimTime){ auto groupNode = _objects[groupNodeName]; auto currentItemBB = groupNode->getBoundingBox(); auto currentItemMidPoint = cocos2d::Point(currentItemBB.getMidX(), currentItemBB.getMidY()); currentItemMidPoint = groupNode->getParent()->convertToWorldSpace(currentItemMidPoint); auto slingUpperPoint = cocos2d::Vec2(_handOriginalPosition.x, _handOriginalPosition.y*2.f); slingUpperPoint = _slingHand->getParent()->convertToWorldSpace(slingUpperPoint); _currentItem = cocos2d::Sprite::create(singleItemFilePath); addChild(_currentItem); _currentItem->setPosition(currentItemMidPoint); _currentItem->runAction(cocos2d::Sequence::create( cocos2d::MoveTo::create(moveAnimTime, slingUpperPoint), cocos2d::CallFunc::create([&](){ placeItemInSling(_currentItem); _slingHand->runAction(cocos2d::MoveBy::create(0.8, cocos2d::Vec2(0, -_slingHand->getContentSize().height*0.5f))); }), nullptr)); auto tutIntroAction = cocos2d::Sequence::create(cocos2d::DelayTime::create(2.2f), cocos2d::CallFunc::create([&](){ shoot();}), nullptr); tutIntroAction->setTag(TutIntroActionTag); runAction(tutIntroAction); } static constexpr char* TUTORIAL_COMPLETE_KEY {"TUTORIAL_COMPLETE_KEY"}; bool SubGameSceneShoot::tutorialComplete(){ return cocos2d::UserDefault::getInstance()->getBoolForKey(TUTORIAL_COMPLETE_KEY, false); } void SubGameSceneShoot::saveTutorialComplete(){ if(!tutorialComplete()){ cocos2d::UserDefault::getInstance()->setBoolForKey(TUTORIAL_COMPLETE_KEY, true); cocos2d::UserDefault::getInstance()->flush(); } } ////