// // HSubGameSceneSpaceInvaders.cpp // SteveAndMaggieGame-mobile // // Created by Katarzyna Kalinowska-Górska on 28/05/2019. // #include #include "HSubGameSceneSpaceInvaders.h" #include "HMiscUtils.h" #include "HMathUtils.h" #include #include "HPetersItemRotateUtils.h" #include "HResourcesConfig.h" #include "HScalingUtils.h" #include "HTwoStateButton.h" #include "HSoundUtils.h" HSubGameSceneSpaceInvaders* HSubGameSceneSpaceInvaders::create(std::string layoutFilePath, CommonConfig commonConfig, std::function HGameConfigParser){ HSubGameSceneSpaceInvaders * scene = new (std::nothrow) HSubGameSceneSpaceInvaders(); if(scene && scene->initWithConfiguration(layoutFilePath,commonConfig, HGameConfigParser)) { scene->autorelease(); return scene; } CC_SAFE_DELETE(scene); return nullptr; } bool HSubGameSceneSpaceInvaders::initWithConfiguration(std::string layoutFilePath, CommonConfig commonConfig, std::function gameConfigParse){ if(!HSubGameScene::initWithConfiguration(layoutFilePath, commonConfig)){ return false; } gameConfigParse(gameConfig); catchingCharacter = createCatchingCharacter(); catchingCharacter->addToParent(this); catchingCharacter->setLocalZOrder(3); catchingCharacter->setOpacity(0); currentMenu = nullptr; spaceInvGameState.interLevelMenuShown = false; clearGameState(); addTouchHandler("spacinTB", prepareTouchBeganHandler(), TOUCHES_BEGAN); addTouchHandler("spacinTM", prepareTouchMovedHandler(), TOUCHES_MOVED); addTouchHandler("spacinTE", prepareTouchEndedHandler(), TOUCHES_ENDED); // if(cocos2d::Application::getInstance()->getTargetPlatform() == cocos2d::ApplicationProtocol::Platform::OS_ANDROID){ // // } // if(cc.sys.isNative && cc.sys.os == cc.sys.OS_ANDROID) // this.createBackButtonListener(); return true; } void HSubGameSceneSpaceInvaders::clearGameState(int level){ catchingCharacter->removeAllCaughtItems(); HSubGameScene::clearGameState(); HMathUtils::shuffleArray(gameState->itemTypeOrder); spaceInvGameState.isGeneratingNewItems = false; spaceInvGameState.isDraggingCharacter = false; resetCharacterPosition(); spaceInvGameState.objectsMissed = 0; gameState->currentLevel = level; spaceInvGameState.currentRound = resetRoundForLevel(level); } void HSubGameSceneSpaceInvaders::resetGame(){ stopAllActions(); clearGameState(); lifeIndicatorView->reset(); startGame(false); } void HSubGameSceneSpaceInvaders::resetGameForLevel(int level){ stopAllActions(); clearGameState(level); lifeIndicatorView->reset(); gameState->currentLevel = level; } HSubGameSceneSpaceInvaders::~HSubGameSceneSpaceInvaders(){ } void HSubGameSceneSpaceInvaders::loadTrees(){ trees.push_back(dynamic_cast(_objects["backgroundTree1Pause"])); trees.push_back(dynamic_cast(_objects["backgroundTree2Pause"])); trees.push_back(dynamic_cast(_objects["backgroundTree3Pause"])); } void HSubGameSceneSpaceInvaders::startBlowingWind(){ // move the trees for(int i = 0; i < trees.size(); ++i){ blowWind(trees[i]); } } void HSubGameSceneSpaceInvaders::blowWind(cocos2d::Sprite* tree){ float skewAngleX = HMathUtils::getRandom(-14, 14); float skewAngleY = HMathUtils::getRandom(-6, 6); // float delay = HMathUtils::getRandom(0, 0.1); float targetSkewX = MAX(MIN(tree->getSkewX()+skewAngleX, 14),0); float targetSkewY = MAX(MIN(tree->getSkewY()+skewAngleY, 8),0); //TODO magic numbers again auto roundIndex = MAX(0,spaceInvGameState.currentRound); auto action = cocos2d::Sequence::create(/*cocos2d::DelayTime::create(delay),*/ cocos2d::EaseInOut::create(cocos2d::SkewTo::create(1/gameConfig.levelConfigs[roundIndex].windParam, targetSkewX, targetSkewY), 1.2), cocos2d::CallFunc::create(std::bind([&](cocos2d::Sprite* pTree){ blowWind(pTree); }, tree)), nullptr); tree->runAction(action); } void HSubGameSceneSpaceInvaders::startBlinkingFinger(){ if(!HMiscUtils::wasMaggieTouched()){ if(hintFingerSprite == nullptr) { hintFingerSprite = cocos2d::Sprite::create( "graphics/g_finger.png"); //TODO to game config addChild(hintFingerSprite, 190); hintFingerSprite->setAnchorPoint(cocos2d::Vec2(1, 0.6)); hintFingerSprite->setRotation(-45); hintFingerSprite->setPositionX(catchingCharacter->getPositionX() - catchingCharacter->getBoundingBox().size.width / 6); hintFingerSprite->setPositionY(catchingCharacter->getPositionY() - catchingCharacter->getBoundingBox().size.height / 10); hintFingerSprite->runAction(cocos2d::RepeatForever::create(cocos2d::Sequence::create( cocos2d::EaseIn::create(cocos2d::FadeIn::create(0.2), 1.2), cocos2d::DelayTime::create(0.2), cocos2d::FadeOut::create(0.4), cocos2d::DelayTime::create(0.2), nullptr))); //TODO magic numbers } } } void HSubGameSceneSpaceInvaders::stopBlinkingFinger(){ if(hintFingerSprite != nullptr){ hintFingerSprite->stopAllActions(); HMiscUtils::hideAndRemoveView(hintFingerSprite, true); hintFingerSprite = nullptr; } } void HSubGameSceneSpaceInvaders::prepareCatchingCharacterEntryRotation(float animationTotalTime){ auto screenSize = cocos2d::Director::getInstance()->getWinSize(); spaceInvGameState.entryRotationParams.catchingCharacterMoveInTotalTime = animationTotalTime; spaceInvGameState.entryRotationParams.totalDisplacement = screenSize.width - screenSize.height/2 + M_PI*screenSize.height/2+M_PI*screenSize.height/4; spaceInvGameState.entryRotationParams.linearVelocity = spaceInvGameState.entryRotationParams.totalDisplacement / spaceInvGameState.entryRotationParams.catchingCharacterMoveInTotalTime; spaceInvGameState.entryRotationParams.currentCatchingCharacterDPhi = 0; spaceInvGameState.entryRotationParams.bigCircleRadius = screenSize.height/2 - catchingCharacter->getBoundingBox().size.height/2; spaceInvGameState.entryRotationParams.midPoint = cocos2d::Point(screenSize.width/2, screenSize.height/2); } void HSubGameSceneSpaceInvaders::resetCharacterPosition(){ catchingCharacter->setFlippedX(false); auto screenSize = cocos2d::Director::getInstance()->getWinSize(); auto position = cocos2d::Point(screenSize.width/2, screenSize.height/2); catchingCharacter->setPosition(position); } void HSubGameSceneSpaceInvaders::onEnter(){ HSubGameScene::onEnter(); prepareBackgrounds(); prepareMenus(); prepareBgMusicButton(); spaceInvGameState.catchingCharacterMovingInPhase = MoveInPhase::NONE; loadTrees(); startBlowingWind(); clearGameState(); //runAction(cocos2d::Sequence::create(cocos2d::DelayTime::create(2), cocos2d::CallFunc::create([&](){ // playRewardVideoForLevel(0); //}), nullptr)); //playRewardVideoForLevel(0); //return; showTOSAcceptPopup([&](){ spaceInvGameState.isShowingLevelPicker = true; // showHLevelPickerLayer(CC_CALLBACK_0(HSubGameSceneSpaceInvaders::startGameWithIntro, this)); showHLevelPickerLayer([&]{ stopAllActions(); HSoundUtils::stopAllSounds(); startGameWithIntro(); }); auto introSoundInfo = intro1Sound(); auto pickLevelSoundInfo = pickLevelSound(); runAction(cocos2d::Sequence::create(cocos2d::CallFunc::create(std::bind([&](std::string introSoundFilePath){ HSoundUtils::playSound(introSoundFilePath); },introSoundInfo.filePath)), cocos2d::DelayTime::create(introSoundInfo.soundDuration + commonConfig.soundConfig.soundPadding)//TODO ,cocos2d::CallFunc::create(std::bind([&](std::string pickLevelSoundPath){ HSoundUtils::playSound(pickLevelSoundPath); repeatPickLevelPrompt(); },pickLevelSoundInfo.filePath)), nullptr)); }); } void HSubGameSceneSpaceInvaders::prepareBgMusicButton(){ auto bgMusicButton = dynamic_cast(_objects["backgroundMusicButton"]); bgMusicButton->setActive(HMiscUtils::shouldBgMusicPlay()); bgMusicButton->setVisible(false); bgMusicButton->setOpacity(0); bgMusicButton->removeFromParent(); addChild(bgMusicButton); bgMusicButton->setLocalZOrder(240); } void HSubGameSceneSpaceInvaders::repeatPickLevelPrompt(){ static int DelaySecs = 15; runAction(cocos2d::Sequence::create(cocos2d::DelayTime::create(DelaySecs), cocos2d::CallFunc::create([&](){ if(spaceInvGameState.isShowingLevelPicker){ HSoundUtils::playSound(pickLevelSound().filePath); repeatPickLevelPrompt(); } }), nullptr)); } void HSubGameSceneSpaceInvaders::prepareBackgrounds(){ _objects["backgroundDarkLayer"]->setOpacity(0); _objects["backgroundPause"]->setCascadeOpacityEnabled(true); } void HSubGameSceneSpaceInvaders::prepareMenus(){ _objects["buttonPanelRepeatPickLevel"]->setVisible(false); _objects["buttonPanelRepeatPickLevel"]->setCascadeOpacityEnabled(true); _objects["buttonPanelRepeat"]->setVisible(false); _objects["buttonPanelRepeat"]->setCascadeOpacityEnabled(true); _objects["replayButton"]->setCascadeOpacityEnabled(true); // dynamic_cast(_objects["levelPicker"])->addOnLevelChangedCallback([](int pickedLevel){ // HMiscUtils::saveLastLevel(pickedLevel); // }); } //void HSubGameSceneSpaceInvaders::prepareLevelPicker(){ // //TODO hardcoded HStrings // std::vector levelImagePaths = {"graphics/levels/level_1.png", // "graphics/levels/level_2.png", // "graphics/levels/level_3.png"}; // levelPicker = HSimpleLevelPickerView::create(levelImagePaths); // addChild(levelPicker); // levelPicker->setPosition(); //} void HSubGameSceneSpaceInvaders::generateFirstItems(int nRows){ for(int i = 0; i < nRows; ++i){ generateItem(i); } } void HSubGameSceneSpaceInvaders::generateItem(ItemRow row){ int itemId; ItemType type; std::string itemPicturePath; auto prefrerredItem = gameState->currentLevel == 0 ? gameState->itemTypeOrder[gameState->currentItemTypeIndex] : ItemType::NONE; if(!generateItemTypeAndId(type, itemId, itemPicturePath, prefrerredItem)){ return; } if(!HScalingUtils::isSmallDevice()){ itemPicturePath = "tablets/" + itemPicturePath; } auto newSprite = cocos2d::Sprite::create(itemPicturePath); auto spriteBB = newSprite->getBoundingBox(); auto rect = generateValidRect(row, spriteBB.size.width, spriteBB.size.height); newSprite->setPosition(rect.origin.x+rect.size.width/2,rect.origin.y+rect.size.height/2); addChild(newSprite); // auto itemPosition = HMathUtils::getRandomInt(0,1); // if(itemPosition > 0){ reorderChild(newSprite, catchingCharacter->getLocalZOrderAbove());//->getLocalZOrderBetween()); // } else { // reorderChild(newSprite, catchingCharacter->getLocalZOrderUnder()); // } SpaceItem* item = new SpaceItem(); item->sprite = newSprite; item->type = type; item->row = row;//assignItemRow(newSprite->getPositionY()); item->velocityModifier = HMathUtils::getRandom(1,2.5) / cocos2d::Director::getInstance()->getContentScaleFactor() * HScalingUtils::getAspectRatioBasedModifierForVelocity(); item->baseY = newSprite->getPositionY(); auto rotationThrow = HMathUtils::getRandom(0, 1); if(rotationThrow > 1 - gameConfig.levelConfigs[spaceInvGameState.currentRound].rotationProbability){ item->rotationPoint = HMathUtils::getRandom(item->sprite->getBoundingBox().size.width, cocos2d::Director::getInstance()->getWinSize().width-item->sprite->getBoundingBox().size.width); } else { item->rotationPoint = cocos2d::Director::getInstance()->getWinSize().width*10; } auto movementTypeThrow = HMathUtils::getRandom(0, 1); if(movementTypeThrow > 1 - gameConfig.levelConfigs[spaceInvGameState.currentRound].sineSum3MovementProbability){ item->movementType = SpaceItem::MovementType::SINE_SUM_3; for(int i = 0; i < 3; ++i){ //TODO MAGIC NUMBER item->sineMovementParams[i].sineShift = HMathUtils::getRandom(0,3.14); item->sineMovementParams[i].sineBreadth = cocos2d::Director::getInstance()->getWinSize().width/HMathUtils::getRandom(8, 16); item->sineMovementParams[i].sineAmpModifier = HMathUtils::getRandom(0.1, gameConfig.levelConfigs[spaceInvGameState.currentRound].sineMovementMaxAmp); } } else if(movementTypeThrow > 1 - gameConfig.levelConfigs[spaceInvGameState.currentRound].sineSum3MovementProbability - gameConfig.levelConfigs[spaceInvGameState.currentRound].sineMovementProbability){ item->movementType = SpaceItem::MovementType::SINE; item->sineMovementParams[0].sineShift = HMathUtils::getRandom(0,3.14); item->sineMovementParams[0].sineBreadth = cocos2d::Director::getInstance()->getWinSize().width/HMathUtils::getRandom(4, 10); item->sineMovementParams[0].sineAmpModifier = HMathUtils::getRandom(0.1, gameConfig.levelConfigs[spaceInvGameState.currentRound].sineMovementMaxAmp); } else { item->movementType = SpaceItem::MovementType::LINEAR; } item->unclippedTexRect = newSprite->getTextureRect(); item->caught = false; item->isBlinking = false; item->isRotating = false; item->passedGenerationBorder = false; gameState->items.insert(std::make_pair(itemId, item)); } HSubGameSceneSpaceInvaders::ItemRow HSubGameSceneSpaceInvaders::removeItem(int itemId, bool& wasCaught){ if(gameState->items.find(itemId) == gameState->items.end()){ return -1; } auto item = dynamic_cast(gameState->items[itemId]); wasCaught = item->caught; if(wasCaught){ catchingCharacter->removeCaughtItem(item); } auto itemRow = item->row; delete gameState->items[itemId]; gameState->items.erase(itemId); return itemRow; } bool HSubGameSceneSpaceInvaders::checkItemCaught(Item* item, bool& isCorrect){ //returns if caught isCorrect = item->type == gameState->itemTypeOrder[gameState->currentItemTypeIndex]; if(gameState->playState != PlayState::PLAYING){ return false; } auto catchingRect = catchingCharacter->getCatchingRect(); auto caughtRect = item->sprite->getBoundingBox(); if(catchingRect.getMinX() <= caughtRect.getMaxX() && catchingRect.getMaxX() >= caughtRect.getMaxX() && catchingRect.getMinY() <= caughtRect.getMidY() && catchingRect.getMaxY() >= caughtRect.getMidY()){ if(caughtRect.size.height > catchingRect.size.height){ item->sprite->setPositionY(catchingRect.getMidY()); } else if(catchingRect.getMinY() > caughtRect.getMinY()){ auto dY = caughtRect.getMinY() - catchingRect.getMinY(); item->sprite->setPositionY(item->sprite->getPositionY() - dY); } else if(catchingRect.getMaxY() < caughtRect.getMaxY()){ auto dY = caughtRect.getMaxY() - catchingRect.getMaxY(); item->sprite->setPositionY(item->sprite->getPositionY() - dY); } reorderChild(item->sprite, catchingCharacter->getLocalZOrderBetween()); return true; } return false; } bool HSubGameSceneSpaceInvaders::checkItemApproachesEndOfScreen(Item* item, float screenFrac){ auto caughtRect = item->sprite->getBoundingBox(); auto winSize = cocos2d::Director::getInstance()->getWinSize(); return caughtRect.getMaxX() >= winSize.width*screenFrac; } bool HSubGameSceneSpaceInvaders::checkRemoveItemCaught(SpaceItem* item){ // assert(item->caught == true); auto catchingRect = catchingCharacter->getCatchingRect(); auto caughtRect = item->sprite->getBoundingBox(); if(catchingRect.getMinX() <= caughtRect.getMinX()){//} && catchingRect.getMinY() <= caughtRect.getMinY() && catchingRect.getMaxY()>=caughtRect.getMaxY()){ // isCorrect = item->type == gameState->itemTypeOrder[gameState->currentItemTypeIndex]; return true; } return false; } void HSubGameSceneSpaceInvaders::adjustItemZOrderIfNeeded(Item* item){ auto catchingRect = catchingCharacter->getCatchingRect(); auto itemRect = item->sprite->getBoundingBox(); if(itemRect.getMaxX() > catchingRect.getMinX() && !(catchingRect.getMinY() <= itemRect.getMinY() && catchingRect.getMaxY() >= itemRect.getMaxY())){ reorderChild(item->sprite, catchingCharacter->getLocalZOrderAbove()); } } cocos2d::Rect HSubGameSceneSpaceInvaders::generateValidRect(ItemRow row, float rectW, float rectH){ auto winSize = cocos2d::Director::getInstance()->getWinSize(); int counter = 16; cocos2d::Rect newRect; do { float x = -rectW; float minY; float maxY; getYRangeForRow(row, minY, maxY); float y = HMathUtils::getRandom(minY, maxY - rectH); newRect = cocos2d::Rect(x, y, rectW, rectH); bool valid = true; std::map::iterator it = gameState->items.begin(); while(it != gameState->items.end()){ auto item = dynamic_cast(it->second); if(newRect.intersectsRect(item->sprite->getBoundingBox())){ valid = false; break; } ++it; } if(valid == true){ break; } else { --counter; } } while(counter > 0); return newRect; //if we've failed to generate a rect that does not intersect other rects, just take what we have } int HSubGameSceneSpaceInvaders::nRows(){ return gameState->currentLevel > 0 ? 4 : 3; } void HSubGameSceneSpaceInvaders::getYRangeForRow(ItemRow row, float& minYIncl, float& maxYExcl){ auto gameHeight = cocos2d::Director::getInstance()->getWinSize().height - gameConfig.topYCutValue; auto totalRows = nRows(); if(totalRows == 4){ switch (row) { case 0: minYIncl = 0; maxYExcl = gameHeight/4; break; case 1: minYIncl = gameHeight/4; maxYExcl = gameHeight/2; break; case 2: minYIncl = gameHeight/2; maxYExcl = gameHeight*0.75; break; case 3: minYIncl = gameHeight*0.75; maxYExcl = gameHeight - gameConfig.topYCutValue; break; default: minYIncl = 0; maxYExcl = 0; break; } } else { switch (row) { case 0: minYIncl = 0; maxYExcl = gameHeight/3; break; case 1: minYIncl = gameHeight/3; maxYExcl = gameHeight/1.5; break; case 2: minYIncl = gameHeight/1.5; maxYExcl = gameHeight - gameConfig.topYCutValue; break; default: minYIncl = 0; maxYExcl = 0; break; } } } void HSubGameSceneSpaceInvaders::update(float dt){ HSubGameScene::update(dt); if(gameState->playState == PlayState::PLAYING){ auto itemsToRemove = updatePhaseMoveGenerateItems(dt); updatePhaseRemoveGenerateItems(itemsToRemove); // now if the level time has run out and we're no longer generating items, wait until all the items disappear and change the level updatePhaseCheckTime(dt); if(!spaceInvGameState.isGeneratingNewItems && gameState->playState != PlayState::LOST){ updatePhaseCheckItemsCount(); } } else if(spaceInvGameState.catchingCharacterMovingInPhase != MoveInPhase::NONE){ updatePhaseMoveInMaggie(dt); } } // returns item ids for items that can be erased std::vector HSubGameSceneSpaceInvaders::updatePhaseMoveGenerateItems(float dt){ auto screenWidth = cocos2d::Director::getInstance()->getWinSize().width; auto velocity = gameConfig.levelConfigs[spaceInvGameState.currentRound].itemVelocity; std::vector itemsToRemove; std::map::iterator it = gameState->items.end(); while (it != gameState->items.begin()) { --it; auto spaceItem = dynamic_cast(it->second); auto sprite = spaceItem->sprite; bool willBeRemoved = false; // remove the item if it has flown outside the screen if(sprite->getBoundingBox().getMinX() > screenWidth){ itemsToRemove.push_back(it->first); willBeRemoved = true; if(it->second->type == gameState->itemTypeOrder[gameState->currentItemTypeIndex]){ handleCorrectItemFlownOutOfScreen(); } } // check for collision with Maggie bool isCorrectItem; if(!spaceItem->caught){ if(checkItemCaught(it->second, isCorrectItem)){ spaceItem->caught = true; spaceItem->caughtDX = spaceItem->sprite->getBoundingBox().getMaxX() - catchingCharacter->getCatchingRect().getMinX(); if(spaceItem->caughtDX > 0){ spaceItem->sprite->setTextureRect(cocos2d::Rect(spaceItem->unclippedTexRect.origin.x, spaceItem->unclippedTexRect.origin.y, MAX(0,spaceItem->sprite->getBoundingBox().size.width - spaceItem->caughtDX), spaceItem->unclippedTexRect.size.height)); // making tex smaller means we need to fix x spaceItem->sprite->setPositionX(spaceItem->sprite->getPositionX()-spaceItem->caughtDX/2); } catchingCharacter->addCaughtItem(spaceItem); reorderChild(spaceItem->sprite, catchingCharacter->getLocalZOrderBetween()); spaceItem->isRotating = false; spaceItem->sprite->setRotation(0); handleCaughtItem(isCorrectItem, spaceItem->type); } else if (isCorrectItem && checkItemApproachesEndOfScreen(spaceItem)) { if(!spaceItem->isBlinking){ spaceItem->isBlinking = true; spaceItem->sprite->setColor(cocos2d::Color3B::RED); spaceItem->sprite->runAction(cocos2d::Blink::create(2, 8)); } } } if(spaceItem->caught){ clipCaughtItem(spaceItem, dt); if(checkRemoveItemCaught(spaceItem)){ itemsToRemove.push_back(it->first); willBeRemoved = true; } } else { updatePhaseMoveUncaughtItem(spaceItem, velocity, dt); if(!spaceItem->isRotating && spaceItem->row != 3 && (spaceItem->type == ItemType::ONE || spaceItem->type == ItemType::FOUR) && spaceItem->sprite->getPositionX() >= spaceItem->rotationPoint){ // only ghosts and witches can do that; with pumpkins and skeletons it doesn't look cool. also, don't broom-sommersault on the highest item level. spaceItem->rotationPoint = screenWidth*10; spaceItem->isRotating = true; spaceItem->rotateTotalAngle = 0; spaceItem->rotateRadius = spaceItem->sprite->getBoundingBox().size.height; //TODO maaybe random? spaceItem->rotateMidPoint = cocos2d::Point(spaceItem->sprite->getPositionX(), spaceItem->sprite->getPositionY()+spaceItem->rotateRadius); } } if(spaceInvGameState.isGeneratingNewItems && !spaceItem->caught &&!spaceItem->passedGenerationBorder && checkItemApproachesEndOfScreen(spaceItem, 0.8)){ spaceItem->passedGenerationBorder = true; generateItem(spaceItem->row); } //check if should reorder if(!willBeRemoved && !(spaceItem->caught)){ adjustItemZOrderIfNeeded(it->second); } } return itemsToRemove; } void HSubGameSceneSpaceInvaders::updatePhaseMoveUncaughtItem(SpaceItem* item, float baseVelocity, float dt){ if(item->isRotating){ // find the angular velocity in radians auto linearVelocity = baseVelocity*item->velocityModifier; auto omega = linearVelocity / item->rotateRadius; // printf("omega: %f, ", omega); // find the angle in radians for dt auto rotationAngle = omega * dt; float dXdYPhi[3]; RotateUtils::nextPosition(dXdYPhi, item->rotateRadius, item->sprite->getPositionX() - item->rotateMidPoint.x, item->sprite->getPositionY() - item->rotateMidPoint.y,rotationAngle); item->sprite->setPosition(item->sprite->getPositionX() + dXdYPhi[0], item->sprite->getPositionY() + dXdYPhi[1]); item->sprite->setRotation(-dXdYPhi[2]*180/M_PI); item->rotateTotalAngle += rotationAngle; if(item->rotateTotalAngle > M_PI*2){ item->isRotating = false; } } else { if(item->movementType == SpaceItem::MovementType::LINEAR){ auto dx = dt*baseVelocity*item->velocityModifier; item->sprite->setPositionX(item->sprite->getPositionX()+dx); } else if(item->movementType == SpaceItem::MovementType::SINE){ auto dx = dt*baseVelocity*item->velocityModifier; item->sprite->setPositionX(item->sprite->getPositionX()+dx); auto dy = item->sprite->getBoundingBox().size.height*item->sineMovementParams[0].sineAmpModifier*sin(item->sprite->getPositionX()/item->sineMovementParams[0].sineBreadth+item->sineMovementParams[0].sineShift); item->sprite->setPositionY(MAX(item->baseY + dy, item->sprite->getBoundingBox().size.height/2*1.3)); } else if(item->movementType == SpaceItem::MovementType::SINE_SUM_3){ auto dx = dt*baseVelocity*item->velocityModifier;//sprite->getBoundingBox().size.width*10; //TODO item->sprite->setPositionX(item->sprite->getPositionX()+dx); auto dy = 0; for(int i = 0; i < 3; ++i){ //TODO magic number dy += item->sprite->getBoundingBox().size.height*item->sineMovementParams[i].sineAmpModifier*sin(item->sprite->getPositionX()/item->sineMovementParams[i].sineBreadth+item->sineMovementParams[i].sineShift); } dy /= 3; item->sprite->setPositionY(MIN(MAX(item->baseY + dy, item->sprite->getBoundingBox().size.height/2*1.3),cocos2d::Director::getInstance()->getWinSize().height - item->sprite->getBoundingBox().size.height/2*1.3));//TODO magic number } } } void HSubGameSceneSpaceInvaders::updatePhaseRemoveGenerateItems(std::vector idsToRemove){ auto it = idsToRemove.begin(); while(it != idsToRemove.end()){ bool wasCaught; auto freeRow = removeItem(*it, wasCaught); if(wasCaught && spaceInvGameState.isGeneratingNewItems){ generateItem(freeRow); } ++it; } } void HSubGameSceneSpaceInvaders::updatePhaseCheckTime(float dt){ gameState->timeCount += dt; if(gameState->timeCount >= gameConfig.levelTimeSeconds){ spaceInvGameState.isGeneratingNewItems = false; gameState->timeCount = 0; } } void HSubGameSceneSpaceInvaders::updatePhaseCheckItemsCount(){ if(gameState->items.size() == 0){ nextLevel(); } } void HSubGameSceneSpaceInvaders::updatePhaseMoveInMaggie(float dt){ //TODO maybe make some separate animation data object or even animation object that will keep track of the temp vars and manage all these calculations if(spaceInvGameState.catchingCharacterMovingInPhase == MoveInPhase::PHASE_LINEAR){ auto dx = spaceInvGameState.entryRotationParams.linearVelocity*dt; auto newPosX = catchingCharacter->getPositionX() - dx; if(newPosX < spaceInvGameState.entryRotationParams.midPoint.x){ // we've reached the "big half-circle" phase spaceInvGameState.entryRotationParams.currentCatchingCharacterDPhi = (spaceInvGameState.entryRotationParams.midPoint.x - newPosX)/spaceInvGameState.entryRotationParams.bigCircleRadius; auto dXCirc = -spaceInvGameState.entryRotationParams.bigCircleRadius*sin(spaceInvGameState.entryRotationParams.currentCatchingCharacterDPhi); auto dYCirc = -spaceInvGameState.entryRotationParams.bigCircleRadius*cos(spaceInvGameState.entryRotationParams.currentCatchingCharacterDPhi); catchingCharacter->setPositionX(spaceInvGameState.entryRotationParams.midPoint.x + dXCirc); catchingCharacter->setPositionY(spaceInvGameState.entryRotationParams.midPoint.y + dYCirc); spaceInvGameState.catchingCharacterMovingInPhase = MoveInPhase::PHASE_BIG_CIRCLE; catchingCharacter->setRotation(spaceInvGameState.entryRotationParams.currentCatchingCharacterDPhi*180/M_PI); } else { catchingCharacter->setPositionX(newPosX); } } else if(spaceInvGameState.catchingCharacterMovingInPhase == MoveInPhase::PHASE_BIG_CIRCLE){ auto dPhi = spaceInvGameState.entryRotationParams.linearVelocity*dt / spaceInvGameState.entryRotationParams.bigCircleRadius; spaceInvGameState.entryRotationParams.currentCatchingCharacterDPhi += dPhi; auto dXCirc = -spaceInvGameState.entryRotationParams.bigCircleRadius*sin(spaceInvGameState.entryRotationParams.currentCatchingCharacterDPhi); auto dYCirc = -spaceInvGameState.entryRotationParams.bigCircleRadius*cos(spaceInvGameState.entryRotationParams.currentCatchingCharacterDPhi); catchingCharacter->setPositionX(spaceInvGameState.entryRotationParams.midPoint.x + dXCirc); catchingCharacter->setPositionY(spaceInvGameState.entryRotationParams.midPoint.y + dYCirc); catchingCharacter->setRotation(spaceInvGameState.entryRotationParams.currentCatchingCharacterDPhi*180/M_PI); if(catchingCharacter->getPositionX() > spaceInvGameState.entryRotationParams.midPoint.x && catchingCharacter->getPositionY() > spaceInvGameState.entryRotationParams.midPoint.y){ auto dPhiSmall = (spaceInvGameState.entryRotationParams.currentCatchingCharacterDPhi - M_PI)*2; spaceInvGameState.catchingCharacterMovingInPhase = MoveInPhase::PHASE_SMALL_CIRCLE; spaceInvGameState.entryRotationParams.currentCatchingCharacterDPhi = M_PI + dPhiSmall; //TODO but also subtract what was on the big half circle side catchingCharacter->setRotation(spaceInvGameState.entryRotationParams.currentCatchingCharacterDPhi*180/M_PI); } } else if(spaceInvGameState.catchingCharacterMovingInPhase == MoveInPhase::PHASE_SMALL_CIRCLE){ auto smallCircleRadius = spaceInvGameState.entryRotationParams.bigCircleRadius/2; spaceInvGameState.entryRotationParams.currentCatchingCharacterDPhi += spaceInvGameState.entryRotationParams.linearVelocity*dt / smallCircleRadius; auto dXCirc = -smallCircleRadius*sin(spaceInvGameState.entryRotationParams.currentCatchingCharacterDPhi); auto dYCirc = -smallCircleRadius*cos(spaceInvGameState.entryRotationParams.currentCatchingCharacterDPhi); auto yMidSmall = spaceInvGameState.entryRotationParams.midPoint.y+smallCircleRadius; catchingCharacter->setPositionX(spaceInvGameState.entryRotationParams.midPoint.x + dXCirc); catchingCharacter->setPositionY(yMidSmall + dYCirc); catchingCharacter->setRotation(spaceInvGameState.entryRotationParams.currentCatchingCharacterDPhi*180/M_PI); if(catchingCharacter->getPositionX() <= spaceInvGameState.entryRotationParams.midPoint.x && catchingCharacter->getPositionY() <= yMidSmall){ spaceInvGameState.catchingCharacterMovingInPhase = MoveInPhase::NONE; catchingCharacter->startCirclingAround(); catchingCharacter->setRotation(0); gameState->playState = PlayState::PRE_PLAYING; } } } //TODO remove minItemXs from gameState HSubGameSceneSpaceInvaders::CatchingCharacter* HSubGameSceneSpaceInvaders::createCatchingCharacter(){ auto bottomImagePath = gameConfig.catchingCharacterBottomImagePath; auto topImagePath = gameConfig.catchingCharacterFrontImagePath; if(!HScalingUtils::isSmallDevice()){ bottomImagePath = "tablets/" + bottomImagePath; topImagePath = "tablets/" + topImagePath; } return CatchingCharacter::create(bottomImagePath, topImagePath, gameConfig.catchingCharacterCatchStartXFrac, gameConfig.catchingCharacterCatchEndXFrac, gameConfig.catchingCharacterCatchBottomYFrac, gameConfig.catchingCharacterCatchTopYFrac); } bool HSubGameSceneSpaceInvaders::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 { resumeGame(); hideSettingsMenu();//WithLevelReset(); } } } else if((objectName == "replayButtonInRepeatPickLevel" || objectName == "replayButton") && touchEventType == cocos2d::ui::Widget::TouchEventType::ENDED){ return this->onReplayButtonClicked(); } else if((objectName == "nextButtonInRepeatPickLevel") && touchEventType == cocos2d::ui::Widget::TouchEventType::ENDED){ onLevelUpButtonClicked(); } else if(objectName == "backgroundMusicButton" && touchEventType == cocos2d::ui::Widget::TouchEventType::ENDED){ auto bgMusicButton = dynamic_cast(_objects[objectName]); if(HMiscUtils::shouldBgMusicPlay()){ HMiscUtils::saveBgMusicPlaying(false); stopBackgroundMusic(false); if(bgMusicButton->isActive()){ bgMusicButton->setActive(false); } } else { HMiscUtils::saveBgMusicPlaying(true); playBackgroundMusic(false); if(!bgMusicButton->isActive()){ bgMusicButton->setActive(true); } } } return HSubGameScene::touchHandlerForWidget(objectName, touchEventType); } bool HSubGameSceneSpaceInvaders::onReplayButtonClicked(){ hideCurrentMenu(); resetGameForLevel(gameState->currentLevel); initCatchingNextItemType(); //playRewardVideoForLevel(1); return true; } bool HSubGameSceneSpaceInvaders::onLevelUpButtonClicked(){ hideCurrentMenu(); auto newLevel = HMiscUtils::nextLevel(); HMiscUtils::saveLastLevel(newLevel); resetGameForLevel(newLevel); initCatchingNextItemType(); return true; } TouchHandlerFunction HSubGameSceneSpaceInvaders::prepareTouchBeganHandler() { return [&](cocos2d::Touch* touch, cocos2d::Event* event){ if(!spaceInvGameState.interLevelMenuShown){ //if((gameState->playState == PlayState::PLAYING || gameState->playState == PlayState::PRE_PLAYING || gameState->playState == PlayState::CHANGING_LEVEL) /*&& !catchingCharacter->isBusy()*/){ auto grabArea = HMiscUtils::getExtendedActiveArea(catchingCharacter, gameConfig.catchingCharacterGrabExtendPercent, gameConfig.catchingCharacterGrabExtendPercent); if(grabArea.containsPoint(touch->getLocation())){ stopBlinkingFinger(); HMiscUtils::saveMaggieTouched(); catchingCharacter->stopAllActions(); spaceInvGameState.isDraggingCharacter = true; spaceInvGameState.dragStartLocation = touch->getLocation(); spaceInvGameState.draggedCharacterStartPosition = catchingCharacter->getPosition(); return true; } } return false; }; } TouchHandlerFunction HSubGameSceneSpaceInvaders::prepareTouchMovedHandler() { return [&](cocos2d::Touch* touch, cocos2d::Event* event){ if(!spaceInvGameState.interLevelMenuShown && spaceInvGameState.isDraggingCharacter && !catchingCharacter->isBusy()){ //if((gameState->playState == PlayState::PLAYING || gameState->playState == PlayState::PRE_PLAYING || gameState->playState == PlayState::CHANGING_LEVEL) && spaceInvGameState.isDraggingCharacter && !catchingCharacter->isBusy()) { auto screenSize = cocos2d::Director::getInstance()->getWinSize(); auto dx = touch->getLocation().x - spaceInvGameState.dragStartLocation.x; auto dy = touch->getLocation().y - spaceInvGameState.dragStartLocation.y; auto minX = screenSize.width*(1-gameConfig.catchingCharacterRangeFrac) + catchingCharacter->getBoundingBox().size.width/2; auto maxX = screenSize.width - catchingCharacter->getBoundingBox().size.width/2; auto minY = catchingCharacter->getBoundingBox().size.height/2; auto maxY = screenSize.height - catchingCharacter->getBoundingBox().size.height/2; auto newPosition = cocos2d::Point(MIN(MAX(spaceInvGameState.draggedCharacterStartPosition.x + dx, minX),maxX), MIN(MAX(spaceInvGameState.draggedCharacterStartPosition.y + dy, minY), maxY)) ; catchingCharacter->setPosition(newPosition); return true; } return false; }; } TouchHandlerFunction HSubGameSceneSpaceInvaders::prepareTouchEndedHandler(){ return [&](cocos2d::Touch* touch, cocos2d::Event* event){ spaceInvGameState.isDraggingCharacter = false; }; } void HSubGameSceneSpaceInvaders::startGame(bool playIntro){ gameState->playState = PlayState::PRE_PLAYING; auto startAction = cocos2d::CallFunc::create([&]{ catchingCharacter->stopCirclingAround(); // startBlinkingFinger(); gameState->currentLevel = (int)HMiscUtils::lastLevel(); spaceInvGameState.currentRound = resetRoundForLevel(gameState->currentLevel); initCatchingNextItemType(); }); if(playIntro){ auto introSoundInfo = intro2Sound(); fadeInBackgroundDarkLayer(introSoundInfo.soundDuration); catchingCharacter->setPosition(cocos2d::Vec2(cocos2d::Director::getInstance()->getWinSize().width+catchingCharacter->getBoundingBox().size.width/2, catchingCharacter->getBoundingBox().size.height/2)); catchingCharacter->setOpacity(255); prepareCatchingCharacterEntryRotation(introSoundInfo.soundDuration/3); spaceInvGameState.catchingCharacterMovingInPhase = MoveInPhase::PHASE_LINEAR; runAction(cocos2d::Sequence::create(cocos2d::CallFunc::create(std::bind([&](std::string introSoundFilePath){ HSoundUtils::playSound(introSoundFilePath); scheduleUpdate(); },introSoundInfo.filePath)), cocos2d::DelayTime::create(introSoundInfo.soundDuration + commonConfig.soundConfig.soundPadding)//TODO , startAction, nullptr)); } else { fadeInBackgroundDarkLayer(); // fadeInPlayBackground(); runAction(startAction); } } void HSubGameSceneSpaceInvaders::handleCaughtItem(bool isCorrectItem, ItemType itemType){ // first stop all actions to prevent saying "it's a yoyo/blocks/etc. after catching the previous object" stopAllActions(); if(isCorrectItem){ handleCorrectItemCaught(itemType); } else { handleIncorrectItemCaught(); } } void HSubGameSceneSpaceInvaders::handleCorrectItemCaught(ItemType type){ HSoundUtils::playSound(HSubGameScene::rightItemEffectSound().filePath); auto hooraySound = HSubGameScene::hooraySound(); if(gameState->currentLevel > 1) { //in the adult level, don't say the whole thing HSoundUtils::playSound(hooraySound.filePath, false); } else { auto confSound = typeConfirmSound(type); runAction(cocos2d::Sequence::create(cocos2d::CallFunc::create(std::bind([&](std::string hooraySoundFilePath){ HSoundUtils::playSound(hooraySoundFilePath, false); },hooraySound.filePath)), cocos2d::DelayTime::create(hooraySound.soundDuration), cocos2d::CallFunc::create(std::bind([&](std::string confSoundFilePath){ HSoundUtils::playSound(confSoundFilePath); },confSound.filePath)), nullptr)); } restoreLife(); } void HSubGameSceneSpaceInvaders::handleIncorrectItemCaught(){ HSoundUtils::playSound(HSubGameScene::wrongItemEffectSound().filePath); auto noSound = HSubGameScene::noSound(); if(gameState->currentLevel > 0) { HSoundUtils::playSound(noSound.filePath, false); } else { // on the baby level, also say the wrong item auto itemType = gameState->itemTypeOrder[gameState->currentItemTypeIndex]; auto wrongSound = typeWrongSound(itemType); runAction(cocos2d::Sequence::create( cocos2d::CallFunc::create(std::bind([&](std::string noSoundFilePath) { HSoundUtils::playSound(noSoundFilePath, false); }, noSound.filePath)), cocos2d::DelayTime::create(noSound.soundDuration), cocos2d::CallFunc::create(std::bind([&](std::string wrongSoundFilePath) { HSoundUtils::playSound(wrongSoundFilePath); }, wrongSound.filePath)), nullptr)); } loseLife(); } void HSubGameSceneSpaceInvaders::handleCorrectItemFlownOutOfScreen(){ // playSound(pureOopsSound().filePath); if(gameState->currentLevel > 0){ // do not penalize for this on baby level loseLife(); } } void HSubGameSceneSpaceInvaders::restoreLife(){ lifeIndicatorView->restoreLife(); spaceInvGameState.objectsMissed = MAX(spaceInvGameState.objectsMissed-1, 0); } void HSubGameSceneSpaceInvaders::loseLife(){ lifeIndicatorView->loseLife(); if (++spaceInvGameState.objectsMissed >= commonConfig.lives) { gameLost(); } } void HSubGameSceneSpaceInvaders::nextLevel(){ gameState->playState = PlayState::CHANGING_LEVEL; stopAllActions(); if(gameState->currentItemTypeIndex == gameState->itemTypeOrder.size()-1){ gameState->playState = PlayState::WON; nextLevelSet(); } else { catchingCharacter->doVictoryJump(); nextItem(); } } void HSubGameSceneSpaceInvaders::nextItem(){ auto wellDoneSound = hooraySound(); runAction(cocos2d::Sequence::create(cocos2d::CallFunc::create(std::bind([&](std::string wellDoneSoundFilePath){ HSoundUtils::playSound(wellDoneSoundFilePath); },wellDoneSound.filePath)), cocos2d::DelayTime::create(wellDoneSound.soundDuration), cocos2d::CallFunc::create([&]{ initCatchingNextItemType(); }), nullptr)); } void HSubGameSceneSpaceInvaders::nextLevelSet(){ unscheduleUpdate(); playRewardVideoForLevel(gameState->currentLevel); // if(spaceInvGameState.currentRound == gameConfig.levelConfigs.size()-1){ // gameWon(); // runAction(cocos2d::Sequence::create(cocos2d::DelayTime::create(1), // cocos2d::CallFunc::create([&](){ // showRepeatMenu(); // }), nullptr));//TODO magic number // // } else { // runAction(cocos2d::Sequence::create(cocos2d::DelayTime::create(1), // cocos2d::CallFunc::create([&](){ // showRepeatNextMenu(); // }), nullptr));//TODO magic number // } } void HSubGameSceneSpaceInvaders::showAppropriateMenu(bool animated){ if(spaceInvGameState.currentRound == gameConfig.levelConfigs.size()-1){ gameWon(); showRepeatMenu(animated); } else { showRepeatNextMenu(animated); } } void HSubGameSceneSpaceInvaders::initCatchingNextItemType(){ gameState->playState = PlayState::PRE_PLAYING; startBlinkingFinger(); spaceInvGameState.objectsMissed = 0; catchingCharacter->runAction(cocos2d::FadeIn::create(HMiscUtils::StandardAnimationTime)); lifeIndicatorView->runAction(cocos2d::FadeIn::create(HMiscUtils::StandardAnimationTime)); lifeIndicatorView->reset(); ++gameState->currentItemTypeIndex; ++spaceInvGameState.currentRound; auto catchSound = typeRequestSound(gameState->itemTypeOrder[gameState->currentItemTypeIndex]); runAction(cocos2d::Sequence::create(cocos2d::CallFunc::create(std::bind([&](std::string catchSoundFilePath){ HSoundUtils::playSound(catchSoundFilePath); },catchSound.filePath)), cocos2d::DelayTime::create(catchSound.soundDuration), cocos2d::CallFunc::create([&]{ //todo here start generating items again generateFirstItems(nRows()); spaceInvGameState.isGeneratingNewItems = true; gameState->playState = PlayState::PLAYING; // catchingCharacter->stopCirclingAround(); // startBlinkingFinger(); setBackgroundMusicFilePath(gameConfig.levelConfigs[spaceInvGameState.currentRound].bgMusicFilePath); if(HMiscUtils::shouldBgMusicPlay()){ playBackgroundMusic(false); } }), nullptr)); scheduleUpdate(); } void HSubGameSceneSpaceInvaders::clipCaughtItem(SpaceItem* item, float dt){ // assert(item->caught == true); auto dXRight = dt*gameConfig.levelConfigs[spaceInvGameState.currentRound].itemVelocity*item->velocityModifier; item->sprite->setTextureRect(cocos2d::Rect(item->unclippedTexRect.origin.x*item->sprite->getScale(), item->unclippedTexRect.origin.y*item->sprite->getScale(), item->sprite->getBoundingBox().size.width - dXRight*item->sprite->getScale(), item->unclippedTexRect.size.height*item->sprite->getScale())); item->sprite->setPositionX(item->sprite->getPositionX()+dXRight/2*item->sprite->getScale()); } void HSubGameSceneSpaceInvaders::gameWon(){ // unscheduleUpdate(); gameState->playState = PlayState::WON; //don't play any sound } void HSubGameSceneSpaceInvaders::gameLost(){ unscheduleUpdate(); HSubGameScene::gameLost(); stopBlinkingFinger(); spaceInvGameState.isGeneratingNewItems = false; catchingCharacter->runAction(cocos2d::FadeOut::create(HMiscUtils::StandardAnimationTime)); for(auto itemIt = gameState->items.begin(); itemIt != gameState->items.end(); ++itemIt){ itemIt->second->sprite->runAction(cocos2d::FadeOut::create(HMiscUtils::StandardAnimationTime)); } showRepeatMenu(); } void HSubGameSceneSpaceInvaders::playRewardVideoForLevel(int levelIndex){ catchingCharacter->runAction(cocos2d::FadeOut::create(2)); //TODO hardcoded number lifeIndicatorView->runAction(cocos2d::FadeOut::create(2)); auto videoIndex = levelIndex+1; std::string videoFilePath = "res/common/reward_videos/reward_video_" + std::to_string(videoIndex) + ".mp4"; playVideo(videoFilePath); } bool HSubGameSceneSpaceInvaders::cleanupVideoPLayerIfNeeded(){ bool videoPLayerCleanedUp = HSubGameScene::cleanupVideoPLayerIfNeeded(); if(videoPLayerCleanedUp) { showAppropriateMenu(); } return videoPLayerCleanedUp; } void HSubGameSceneSpaceInvaders::videoPLayerFinishedPlayback(){ HSubGameScene::videoPLayerFinishedPlayback(); showAppropriateMenu(); } void HSubGameSceneSpaceInvaders::fadeInBackgroundDarkLayer(float time){ auto bgDarkLayer = _objects["backgroundDarkLayer"]; bgDarkLayer->runAction(cocos2d::FadeIn::create(time)); } void HSubGameSceneSpaceInvaders::pauseGame(){ unscheduleUpdate(); HSoundUtils::pauseMusic(); HSoundUtils::pauseAllSounds(); pause(); catchingCharacter->pause(); if(catchingCharacter->additionalFrontSprite != nullptr){ catchingCharacter->additionalFrontSprite->pause(); } if(hintFingerSprite != nullptr){ hintFingerSprite->pause(); } // gameState->playState = HSubGameScene::PlayState::PAUSED; } void HSubGameSceneSpaceInvaders::resumeGame(){ if(gameState->playState == HSubGameScene::PlayState::PLAYING) { auto catchSound = typeRequestSound( gameState->itemTypeOrder[gameState->currentItemTypeIndex]); HSoundUtils::playSound(catchSound.filePath); } scheduleUpdate(); HSoundUtils::resumeMusic(); HSoundUtils::resumeAllSounds(); resume(); catchingCharacter->resume(); if(catchingCharacter->additionalFrontSprite != nullptr){ catchingCharacter->additionalFrontSprite->resume(); } if(hintFingerSprite != nullptr){ hintFingerSprite->resume(); } // gameState->playState = HSubGameScene::PlayState::PLAYING; } void HSubGameSceneSpaceInvaders::hideSettingsMenuWithLevelReset(){ resumeGame(); hideSettingsMenu(true); auto newLevel = (int)(HMiscUtils::lastLevel()); if(newLevel != gameState->currentLevel){ hideCurrentMenu(); resetGameForLevel(newLevel); initCatchingNextItemType(); } } void HSubGameSceneSpaceInvaders::showSettingsMenu(bool animated){ HSubGameScene::showSettingsMenu(animated); HMiscUtils::showView(_objects["backgroundMusicButton"], animated); } void HSubGameSceneSpaceInvaders::hideSettingsMenu(bool animated){ HSubGameScene::hideSettingsMenu(animated); HMiscUtils::hideView(_objects["backgroundMusicButton"], animated); } void HSubGameSceneSpaceInvaders::hideCurrentMenu(bool animated){ spaceInvGameState.interLevelMenuShown = false; stopMenuAnimations(); if(currentMenu != nullptr){ auto tempNode = currentMenu; currentMenu = nullptr; if(animated){ tempNode->runAction(cocos2d::Sequence::create(cocos2d::FadeOut::create(HMiscUtils::StandardAnimationTime), cocos2d::CallFunc::create(std::bind([&](cocos2d::Node* menuToHide){ menuToHide->setVisible(false); resetMenuButtonYs(); }, tempNode)), nullptr)); } else { currentMenu->setVisible(false); } } } void HSubGameSceneSpaceInvaders::showRepeatMenu(bool animated){ auto menu = _objects["buttonPanelRepeat"]; auto HLevelViewToBeShown = dynamic_cast(_objects["level1Pic"]); HLevelViewToBeShown->changePicture(HResourcesConfig::picturePathForLevel(gameState->currentLevel)); showMenu(menu, animated); } void HSubGameSceneSpaceInvaders::showRepeatNextMenu(bool animated){ auto menu = _objects["buttonPanelRepeatPickLevel"]; auto firstHLevelViewToBeShown = dynamic_cast(_objects["level2Pic"]); firstHLevelViewToBeShown->changePicture(HResourcesConfig::picturePathForLevel(gameState->currentLevel)); auto secondLevelToBeShown = dynamic_cast(_objects["level3Pic"]); secondLevelToBeShown->changePicture(HResourcesConfig::picturePathForLevel(gameState->currentLevel+1)); showMenu(menu, animated); } void HSubGameSceneSpaceInvaders::showMenu(cocos2d::Node* menuNode, bool animated){ if(currentMenu != menuNode) { hideCurrentMenu(animated); spaceInvGameState.interLevelMenuShown = true; currentMenu = menuNode; if (animated) { catchingCharacter->runAction( cocos2d::FadeOut::create(HMiscUtils::StandardAnimationTime)); lifeIndicatorView->runAction( cocos2d::FadeOut::create(HMiscUtils::StandardAnimationTime)); menuNode->setVisible(true); menuNode->setOpacity(0); menuNode->runAction(cocos2d::FadeIn::create(HMiscUtils::StandardAnimationTime)); } else { catchingCharacter->setOpacity(0); lifeIndicatorView->setOpacity(0); menuNode->setVisible(true); } startMenuAnimations(); // runAction(cocos2d::Sequence::create(cocos2d::DelayTime::create(HMiscUtils::StandardAnimationTime*2), cocos2d::CallFunc::create([&]{ // startMenuAnimations(); // }), NULL)); } } void HSubGameSceneSpaceInvaders::startMenuAnimations(){ runAction(cocos2d::Sequence::create(cocos2d::DelayTime::create(2), cocos2d::CallFunc::create([&]{ auto winSize = cocos2d::Director::getInstance()->getWinSize(); catchingCharacter->setPositionX(winSize.width + catchingCharacter->getBoundingBox().size.width/2); catchingCharacter->setOpacity(255); runMaggieMenuAnimations(); }), NULL)); runButtonAnimations(); runLevelGhostsAnimations(); } void HSubGameSceneSpaceInvaders::runMaggieMenuAnimations(){ auto winSize = cocos2d::Director::getInstance()->getWinSize(); auto x = catchingCharacter->getPositionX() > winSize.width ? -catchingCharacter->getBoundingBox().size.width/2 : winSize.width + catchingCharacter->getBoundingBox().size.width/2; catchingCharacter->setFlippedX(x > 0); auto y = HMathUtils::getRandom(catchingCharacter->getBoundingBox().size.height/2, winSize.height-catchingCharacter->getBoundingBox().size.height/2); auto nextPoint = cocos2d::Point(x, y); auto delay = HMathUtils::getRandom(5, 8); catchingCharacter->runAction(cocos2d::Sequence::create(cocos2d::DelayTime::create(delay), cocos2d::MoveTo::create(3, nextPoint), cocos2d::CallFunc::create([&](){ runMaggieMenuAnimations(); }), nullptr)); catchingCharacter->additionalFrontSprite->runAction(cocos2d::Sequence::create(cocos2d::DelayTime::create(delay), cocos2d::MoveTo::create(3, nextPoint), nullptr)); } void HSubGameSceneSpaceInvaders::runButtonAnimations(){ auto menu = currentMenu; std::vector buttons; std::vector ghosts; if(menu != nullptr){ if(dynamic_cast(menu) != nullptr){ buttons.push_back(menu); } else { for(auto child : menu->getChildren()){ if(dynamic_cast(child) != nullptr){ buttons.push_back(child); } } } for(auto child : menu->getChildren()){ auto childHLevelView = dynamic_cast(child); if(childHLevelView != nullptr){ ghosts.push_back(childHLevelView); } } } for(int i = 0; i < buttons.size(); ++i){ auto button = buttons[i]; auto corrGhost = ghosts[i]; // TODO MAGIC NUMBERS auto delay = HMathUtils::getRandom(3.5, 6); runAction(cocos2d::RepeatForever::create(cocos2d::Sequence::create(cocos2d::DelayTime::create(delay), cocos2d::CallFunc::create(std::bind([&](cocos2d::Node* button, HLevelView* corrHLevelView){ button->runAction(cocos2d::JumpTo::create(0.6, button->getPosition(), button->getBoundingBox().size.height*0.2, 2)); corrHLevelView->flash(); }, button, corrGhost)), nullptr))); } } void HSubGameSceneSpaceInvaders::runLevelGhostsAnimations(){ auto menu = currentMenu; std::vector ghosts; if(menu != nullptr){ for(auto child : menu->getChildren()){ auto childHLevelView = dynamic_cast(child); if(childHLevelView != nullptr){ ghosts.push_back(childHLevelView); } } } for(auto ghost : ghosts){ // TODO MAGIC NUMBERS auto delay = HMathUtils::getRandom(0, 1); runAction(cocos2d::Sequence::create(cocos2d::DelayTime::create(delay), cocos2d::CallFunc::create(std::bind([&](HLevelView* HLevelView){ HLevelView->startFloating(); }, ghost)), nullptr)); } } void HSubGameSceneSpaceInvaders::stopMenuAnimations(){ catchingCharacter->stopAllActions(); // if(catchingCharacter->additionalFrontSprite != nullptr){ // catchingCharacter->additionalFrontSprite->stopAllActions(); // } if(currentMenu != nullptr){ currentMenu->stopAllActions(); for(auto child : currentMenu->getChildren()){ child->stopAllActions(); } } stopAllActions(); } void HSubGameSceneSpaceInvaders::resetMenuButtonYs(){ if(currentMenu != nullptr){ auto baseY = cocos2d::Director::getInstance()->getWinSize().height/2; currentMenu->setPositionY(baseY); for(auto child : currentMenu->getChildren()){ child->setPositionY(baseY); } } }