// // HSubGameSceneSpaceInvaders.h // SteveAndMaggieGame // // Created by Katarzyna Kalinowska-Górska on 28/05/2019. // #ifndef HSubGameSceneSpaceInvaders_h #define HSubGameSceneSpaceInvaders_h #include "cocos2d.h" #include "HSubGameScene.h" #include "HTouchHandlerTypes.h" #include #include #include "HMathUtils.h" #include "HLevelPickerView.h" #include "HMiscUtils.h" class HSubGameSceneSpaceInvaders : public HSubGameScene { public: struct LevelConfig { float itemVelocity; float sineMovementMaxAmp; float sineMovementProbability; float sineSum3MovementProbability; float windParam; float rotationProbability; std::string bgMusicFilePath; }; struct GameConfig { std::vector levelConfigs; float catchingCharacterRangeFrac; //how far to the left can we move the catching character (unit:fraction of the screen width) float catchingCharacterCatchStartXFrac; //where the sack begins and we can talk about "catching" the item float catchingCharacterCatchTopYFrac; float catchingCharacterCatchBottomYFrac; float catchingCharacterCatchEndXFrac; // when the item passes that x, it can no longer be caught std::string catchingCharacterBottomImagePath; std::string catchingCharacterFrontImagePath; int catchingCharacterGrabExtendPercent; float topYCutValue; float levelTimeSeconds; }; static HSubGameSceneSpaceInvaders* create(std::string layoutFilePath, CommonConfig commonConfig, std::function HGameConfigParser); virtual ~HSubGameSceneSpaceInvaders(); virtual void onEnter() override; virtual void update(float dt) override; virtual void pauseGame() override; virtual void resumeGame() override; virtual bool cleanupVideoPLayerIfNeeded() override; protected: typedef int ItemRow; class SpaceItem : public Item { public: enum class MovementType { LINEAR, SINE, SINE_SUM_3 }; struct SineMovementParams { float sineShift; float sineBreadth; float sineAmpModifier; }; ItemRow row; MovementType movementType; float velocityModifier; float baseY; SineMovementParams sineMovementParams[3]; float rotationPoint; bool isRotating; float rotateRadius; float rotateTotalAngle; cocos2d::Point rotateMidPoint; cocos2d::Rect unclippedTexRect; bool caught; // caught already and can't get out regardless of maggie's movement. should move along with maggie float caughtDX; bool isBlinking; bool passedGenerationBorder; virtual ~SpaceItem(){ // Item::~Item(); } }; class CatchingCharacter : public cocos2d::Sprite { public: static CatchingCharacter* create(std::string bottomSpritePath, std::string frontSpritePath, float minCatchX, float maxCatchX, float bottomCatchY, float topCatchY){ CatchingCharacter * character = new (std::nothrow) CatchingCharacter(); if(character && character->init(bottomSpritePath,frontSpritePath, minCatchX, maxCatchX, bottomCatchY, topCatchY)) { character->autorelease(); return character; } CC_SAFE_DELETE(character); return nullptr; } virtual int getLocalZOrderUnder(){ return getLocalZOrder()-1; } virtual int getLocalZOrderBetween(){ return getLocalZOrder()+1; } virtual int getLocalZOrderAbove(){ return getLocalZOrder()+3; } virtual cocos2d::Rect getCatchingRect(){ auto catchingRect = getBoundingBox(); catchingRect.origin.x += minCatchX*catchingRect.size.width; catchingRect.size.width = (maxCatchX-minCatchX)*catchingRect.size.width; catchingRect.origin.y += bottomCatchY*catchingRect.size.height; catchingRect.size.height -= (bottomCatchY+1-topCatchY)*catchingRect.size.height; return catchingRect; } ~CatchingCharacter(){ if(additionalFrontSprite != nullptr){ additionalFrontSprite->removeFromParent(); additionalFrontSprite->release(); } } virtual void addToParent(cocos2d::Node* parent){ parent->addChild(this); if(additionalFrontSprite != nullptr){ parent->addChild(additionalFrontSprite); additionalFrontSprite->setPosition(getBoundingBox().size.width/2, getBoundingBox().size.height/2); additionalFrontSprite->setLocalZOrder(getLocalZOrder()+2); } } virtual void setPosition(const cocos2d::Vec2& position) override { auto prevPosition = getPosition(); cocos2d::Sprite::setPosition(position); if(additionalFrontSprite != nullptr){ additionalFrontSprite->setPosition(position); } auto dX = getPositionX() - prevPosition.x; auto dY = getPositionY() - prevPosition.y; for(auto itemIt = caughtItems.begin(); itemIt != caughtItems.end(); ++itemIt){ auto newPositionY = MIN((*itemIt)->sprite->getBoundingBox().getMaxY(), (*itemIt)->sprite->getPositionY()+dY); newPositionY = MAX((*itemIt)->sprite->getBoundingBox().getMinY(), newPositionY); (*itemIt)->sprite->setPositionY(newPositionY); auto newPositionX = (*itemIt)->sprite->getPositionX() + dX; (*itemIt)->sprite->setPositionX(newPositionX); } } virtual void setPositionX(float positionX) override { auto prevPosition = getPositionX(); cocos2d::Sprite::setPositionX(positionX); if(additionalFrontSprite != nullptr){ additionalFrontSprite->setPositionX(positionX); } auto dX = getPositionX() - prevPosition; for(auto itemIt = caughtItems.begin(); itemIt != caughtItems.end(); ++itemIt){ auto newPositionX = (*itemIt)->sprite->getPositionX() + dX; (*itemIt)->sprite->setPositionX(newPositionX); } } virtual void setPositionY(float positionY) override { auto prevPosition = getPositionY(); cocos2d::Sprite::setPositionY(positionY); if(additionalFrontSprite != nullptr){ additionalFrontSprite->setPositionY(positionY); } auto dY = getPositionY() - prevPosition; for(auto itemIt = caughtItems.begin(); itemIt != caughtItems.end(); ++itemIt){ auto newPositionY = MIN((*itemIt)->sprite->getBoundingBox().getMaxY(), (*itemIt)->sprite->getPositionY()+dY); newPositionY = MAX((*itemIt)->sprite->getBoundingBox().getMinY(), newPositionY); (*itemIt)->sprite->setPositionY(newPositionY); } } virtual void setRotation(float rotation) override { cocos2d::Sprite::setRotation(rotation); if(additionalFrontSprite != nullptr){ additionalFrontSprite->setRotation(rotation); } } virtual void setAnchorPoint(const cocos2d::Vec2& position) override { cocos2d::Sprite::setAnchorPoint(position); if(additionalFrontSprite != nullptr){ additionalFrontSprite->setAnchorPoint(position); } } virtual void setLocalZOrder(std::int32_t zOrder) override{ cocos2d::Sprite::setLocalZOrder(zOrder); if(additionalFrontSprite != nullptr){ additionalFrontSprite->setLocalZOrder(zOrder+2); } } virtual void setScale(float scale) override { cocos2d::Sprite::setScale(scale); if(additionalFrontSprite != nullptr){ additionalFrontSprite->setScale(scale); } } virtual void setOpacity(uint8_t opacity) override { cocos2d::Sprite::setOpacity(opacity); if(additionalFrontSprite != nullptr){ additionalFrontSprite->setOpacity(opacity); } } virtual void setFlippedX(bool flippedX) override { cocos2d::Sprite::setFlippedX(flippedX); if(additionalFrontSprite != nullptr){ additionalFrontSprite->setFlippedX(flippedX); } } virtual void addCaughtItem(SpaceItem* item){ caughtItems.push_back(item); } virtual void removeCaughtItem(SpaceItem* item){ auto itemIt = std::find(caughtItems.begin(), caughtItems.end(), item); if(itemIt != caughtItems.end()){ caughtItems.erase(itemIt); } } virtual void removeAllCaughtItems(){ caughtItems.clear(); } virtual void startCirclingAround(){ isCirclingAround = true; circleMidPoint = getPosition(); previousDirection = 1; circleAround(); } virtual void stopCirclingAround(){ if(isCirclingAround){ isCirclingAround = false; stopAllActions(); //TODO by tag if(additionalFrontSprite != nullptr){ additionalFrontSprite->stopAllActions(); } } } virtual void circleAround(){ float radiusX = getBoundingBox().size.height/100; float radiusY = getBoundingBox().size.height/16; float newX = circleMidPoint.x + HMathUtils::getRandom(-radiusX, radiusX); float newY = circleMidPoint.y + radiusY*previousDirection; auto animationTime = 0.6;//*(dX*dX+dY*dY)/(radiusX*radiusX+radiusY*radiusY); auto floatAction = cocos2d::Sequence::create(cocos2d::EaseInOut::create(cocos2d::MoveTo::create(animationTime, cocos2d::Vec2(newX, newY)), 2), cocos2d::CallFunc::create([&](){ circleAround(); }), nullptr); runAction(floatAction); if(additionalFrontSprite != nullptr){ additionalFrontSprite->runAction(cocos2d::EaseInOut::create(cocos2d::MoveTo::create(animationTime, cocos2d::Vec2(newX, newY)),2)); } previousDirection *= -1; } virtual void doVictoryJump(){ isDoingVictoryJump = true; auto animTime = 0.7f; auto reEnableAfterTime = 0.2f; auto jumpHeight = MIN(cocos2d::Director::getInstance()->getWinSize().height - getPositionY() - getBoundingBox().size.height/2, getBoundingBox().size.height); runAction(cocos2d::Sequence::create(cocos2d::Spawn::create(cocos2d::JumpTo::create(animTime, getPosition(), jumpHeight, 1), cocos2d::RotateBy::create(animTime, 360), nullptr), cocos2d::DelayTime::create(reEnableAfterTime), cocos2d::CallFunc::create([&](){ isDoingVictoryJump = false; }), nullptr)); } virtual bool isBusy(){ return isDoingVictoryJump; } cocos2d::Sprite* additionalFrontSprite; virtual void stopAllActions() override { cocos2d::Sprite::stopAllActions(); setRotation(0); if(additionalFrontSprite != nullptr){ additionalFrontSprite->stopAllActions(); additionalFrontSprite->setRotation(0); } isDoingVictoryJump = false; isCirclingAround = false; } protected: // cocos2d::Sprite* additionalFrontSprite; float minCatchX; float maxCatchX; float bottomCatchY; float topCatchY; std::vector caughtItems; cocos2d::Point circleMidPoint; int previousDirection; bool isDoingVictoryJump; bool isCirclingAround; virtual bool init(std::string bottomSpritePath, std::string frontSpritePath, float minCatchX, float maxCatchX, float bottomCatchY, float topCatchY){ if(!cocos2d::Sprite::initWithFile(bottomSpritePath)){ return false; } additionalFrontSprite = cocos2d::Sprite::create(frontSpritePath); if(additionalFrontSprite != nullptr){ additionalFrontSprite->retain(); } this->minCatchX = minCatchX; this->maxCatchX = maxCatchX; this->bottomCatchY = bottomCatchY; this->topCatchY = topCatchY; isCirclingAround = false; isDoingVictoryJump = false; return true; } }; CatchingCharacter* catchingCharacter; enum class MoveInPhase { NONE, PHASE_LINEAR, PHASE_BIG_CIRCLE, PHASE_SMALL_CIRCLE }; struct EntryRotationParams { float currentCatchingCharacterDPhi; float catchingCharacterMoveInTotalTime; float totalDisplacement; float linearVelocity; float bigCircleRadius; cocos2d::Point midPoint; }; struct SpaceInvGameState { int currentRound; bool isCatchingCharacterCircling; MoveInPhase catchingCharacterMovingInPhase; EntryRotationParams entryRotationParams; bool isGeneratingNewItems; bool isDraggingCharacter; cocos2d::Point dragStartLocation; cocos2d::Point draggedCharacterStartPosition; int objectsMissed; bool isShowingLevelPicker; bool interLevelMenuShown; }; GameConfig gameConfig; SpaceInvGameState spaceInvGameState; std::vector trees; cocos2d::Sprite* hintFingerSprite; cocos2d::Node* currentMenu; // HSimpleLevelPickerView* levelPicker; virtual bool initWithConfiguration(std::string layoutFilePath, CommonConfig commonConfig, std::function HGameConfigParser); virtual void clearGameState(int level = 0); virtual void resetGame() override; virtual void resetGameForLevel(int level); virtual void resetCharacterPosition(); virtual void loadTrees(); virtual CatchingCharacter* createCatchingCharacter(); virtual TouchHandlerFunction prepareTouchBeganHandler(); virtual TouchHandlerFunction prepareTouchMovedHandler(); virtual TouchHandlerFunction prepareTouchEndedHandler(); virtual void prepareBackgrounds(); virtual void prepareMenus(); virtual void prepareBgMusicButton(); virtual bool touchHandlerForWidget(std::string objectName, cocos2d::ui::Widget::TouchEventType touchEventType) override; virtual bool onReplayButtonClicked() override; virtual bool onLevelUpButtonClicked(); virtual void startBlowingWind(); virtual void blowWind(cocos2d::Sprite* tree); virtual void startBlinkingFinger(); virtual void stopBlinkingFinger(); virtual void prepareCatchingCharacterEntryRotation(float animationTotalTime); virtual void generateFirstItems(int nRows); virtual void generateItem(ItemRow row); virtual ItemRow removeItem(int itemId, bool& wasCaught); virtual int nRows(); virtual cocos2d::Rect generateValidRect(ItemRow row, float rectW, float rectH); virtual void getYRangeForRow(ItemRow row, float& minYIncl, float& maxYExcl); virtual bool checkItemCaught(Item* item, bool& isCorrect); //returns if caught // virtual bool checkItemPassedCatchingCharacter(Item* item); virtual bool checkItemApproachesEndOfScreen(Item* item, float screenFrac = 0.9f); virtual void loseLife(); virtual void restoreLife(); virtual bool checkRemoveItemCaught(SpaceItem* item); virtual void clipCaughtItem(SpaceItem* item, float dt); virtual void adjustItemZOrderIfNeeded(Item* item); virtual void handleCaughtItem(bool isCorrectItem, ItemType itemType); virtual void handleCorrectItemCaught(ItemType itemType); virtual void handleIncorrectItemCaught(); virtual void nextLevel(); virtual void nextItem(); virtual void nextLevelSet(); virtual void showAppropriateMenu(bool animated = true); virtual void initCatchingNextItemType(); virtual void handleCorrectItemFlownOutOfScreen(); virtual void fadeInBackgroundDarkLayer(float time = 0.4); int resetRoundForLevel(int level){ return (int)gameState->itemTypeOrder.size()*level-1; } // virtual void startGame(bool playIntro = true) override; void startGameWithIntro() { stopAllActions(); spaceInvGameState.isShowingLevelPicker = false; startGame(true);} virtual void gameWon() override; virtual void gameLost() override; virtual void playRewardVideoForLevel(int levelIndex); virtual void videoPLayerFinishedPlayback() override; virtual std::vector updatePhaseMoveGenerateItems(float dt); virtual void updatePhaseRemoveGenerateItems(std::vector idsToRemove); virtual void updatePhaseCheckTime(float dt); virtual void updatePhaseCheckItemsCount(); virtual void updatePhaseMoveUncaughtItem(SpaceItem* item, float baseVelocity, float dt); virtual void updatePhaseMoveInMaggie(float dt); virtual void hideSettingsMenuWithLevelReset() override; virtual void showSettingsMenu(bool animated = true) override; virtual void hideSettingsMenu(bool animated = true) override; virtual void hideCurrentMenu(bool animated = true); virtual void showRepeatMenu(bool animated = true); virtual void showRepeatNextMenu(bool animated = true); virtual void showMenu(cocos2d::Node* menuNode, bool animated = true); void startMenuAnimations(); void runMaggieMenuAnimations(); void runButtonAnimations(); void runLevelGhostsAnimations(); void resetMenuButtonYs(); void stopMenuAnimations(); void repeatPickLevelPrompt(); }; #endif /* HSubGameSceneSpaceInvaders_h */