MapAdventureObjectRotatingEnd.cpp 7.73 KB
//
//  MapAdventureObjectSlide.cpp
//  WattsenglishFoodApp
//
//  Created by Katarzyna Kalinowska-Górska on 21/03/2020.
//

#include "AniMapUtils.h"
#include "MapAdventureObjectRotatingEnd.h"
#include "AniScalingUtils.h"
#include "AniGeometryUtils.h"
#include "AniMiscUtils.h"

MapAdventureObjectRotatingEnd::MapAdventureObjectRotatingEnd(const rapidjson::Value& p_mapObjectData, IMapImageObject* p_mapImageObject, IMapImageObject* p_hintMapImageObject) : IMapAdventureObject(p_mapObjectData) {
    m_objectClassName = "MapAdventureObjectRotatingEnd";
    
    auto scale = 1/cocos2d::Director::getInstance()->getContentScaleFactor();
    m_mapImageObjects.insert({p_mapImageObject->objectName, p_mapImageObject});
    m_occupiedTilesLying = AniJSONParseUtilsMap::parseTileDataArray(p_mapObjectData["occupiedPointsLying"]);
    m_occupiedTilesReady = AniJSONParseUtilsMap::parseTileDataArray(p_mapObjectData["occupiedPointsReady"]);
    m_entryTilesLying = AniJSONParseUtilsMap::parseTileDataArray(p_mapObjectData["entryPointsLying"]);
    m_entryTilesReady = AniJSONParseUtilsMap::parseTileDataArray(p_mapObjectData["entryPointsReady"]);
    m_tempBlockedTiles = AniJSONParseUtilsMap::parseTileDataArray(p_mapObjectData["tempBlockedTiles"]);
    m_wasAlwaysReady = m_isReady = p_mapObjectData["isReady"].GetBool();
    m_rotationPivotPoint = AniJSONParseUtils::getPoint(p_mapObjectData["rotationPivotPoint"])*scale;
    m_readyRotationDegrees = p_mapObjectData["readyRotationDegrees"].GetInt();
    m_blinkPictureFilePath = p_mapObjectData.HasMember("blinkPictureFilePath") ? p_mapObjectData["blinkPictureFilePath"].GetString() : "";
    m_originalPictureFilePath = p_mapObjectData.HasMember("originalPictureFilePath") ? p_mapObjectData["originalPictureFilePath"].GetString() : "";
    m_mergesWithMapWhenReady = p_mapObjectData.HasMember("mergesWithMapWhenReady") ? p_mapObjectData["mergesWithMapWhenReady"].GetBool() : false;
    if(p_hintMapImageObject != nullptr){
        m_hintMapImageObject = p_hintMapImageObject;
        m_hintMapImageObject->setOpacity(0);
    }
    auto originalAnchorPoint = p_mapImageObject->getAnchorPoint();
    p_mapImageObject->setAnchorPoint(cocos2d::Point(m_rotationPivotPoint.x / p_mapImageObject->getBoundingBox().size.width, m_rotationPivotPoint.y / p_mapImageObject->getBoundingBox().size.height));
    // when we change the anchor point, we also need to adjust the postion so the sprite stays at the same spot
    auto anchorPointChange = p_mapImageObject->getAnchorPoint() - originalAnchorPoint;
    p_mapImageObject->setPositionX(p_mapImageObject->getPositionX() + anchorPointChange.x*p_mapImageObject->getBoundingBox().size.width);
    p_mapImageObject->setPositionY(p_mapImageObject->getPositionY() + anchorPointChange.y*p_mapImageObject->getBoundingBox().size.height);
    if(m_isReady){
        p_mapImageObject->setRotation(m_readyRotationDegrees);
        p_mapImageObject->setMergeWithBackground(m_mergesWithMapWhenReady);
    } else {
        p_mapImageObject->setRotation(p_mapObjectData["entryRotationDegrees"].GetInt());
    }
    m_originalRotation = p_mapImageObject->getRotation();
}

std::vector<AniMapUtils::TileData> MapAdventureObjectRotatingEnd::getEntryTiles() const  {
    if(m_isReady){
        return m_entryTilesReady;
    } else {
        return m_entryTilesLying;
    }
}

std::vector<AniMapUtils::TileData> MapAdventureObjectRotatingEnd::getOccupiedTiles() const {
    return m_isReady ? m_occupiedTilesReady : m_occupiedTilesLying;
}

void MapAdventureObjectRotatingEnd::rotate(cocos2d::Point p_touchPointOnMap){
    if(!m_isReady){
        auto rotateAngleDegrees = evalNewRotation(p_touchPointOnMap);
        if(evalReady(rotateAngleDegrees)){
            getMapImageObject()->setRotation(m_readyRotationDegrees);
            m_isReady = true;
            performOnRotationComplete();
            if(m_mergesWithMapWhenReady){
                getMapImageObject()->setMergeWithBackground(true);
            }
            if(m_hintMapImageObject != nullptr){
                m_hintMapImageObject->setOpacity(0);
            }
            if(m_delegate != nullptr){
                m_delegate->onOccupiedTilesDataChanged(this);
                m_delegate->onBlockedTilesDataChanged(this, m_tempBlockedTiles, std::vector<AniMapUtils::TileData>());
            }
        } else {
            getMapImageObject()->setRotation(rotateAngleDegrees);
        }
    }
}

float MapAdventureObjectRotatingEnd::evalNewRotation(cocos2d::Point p_touchPointOnMap){
    auto rotationPivotPointInMapCoords = getMapImageObject()->getPosition(); //the anchor point is already set as m_rotationPivotPoint
    auto rotateVec = p_touchPointOnMap - rotationPivotPointInMapCoords;
    auto rotateAngle = -atan2(rotateVec.y, rotateVec.x);
    auto fix = 0.f;
    if(getMapImageObject()->getAnchorPoint().x > 0.5){
        fix = 180;
    }
    auto rotateAngleDegrees = rotateAngle*180.0/3.14 + fix;
    return ensureValidRotation(rotateAngleDegrees, getMapImageObject()->getRotation());
}

bool MapAdventureObjectRotatingEnd::evalReady(float p_newRotationDegrees){
    static float eps = 4.f;
    return abs(p_newRotationDegrees - m_readyRotationDegrees) <= eps;
}

bool MapAdventureObjectRotatingEnd::evalReady(cocos2d::Point p_touchPointOnMap){
    auto newRotation = evalNewRotation(p_touchPointOnMap);
    return evalReady(newRotation);
}

void MapAdventureObjectRotatingEnd::resetRotation(){
    getMapImageObject()->setRotation(m_originalRotation);
}

//todo make somehow generic?
//todo force to blink upon interacting? instead of doing it in the character controller?
static int MAOREBlinkActionTag = 10; //temp
//TODO better blinking
void MapAdventureObjectRotatingEnd::startBlinking(){
    
    if(m_blinkPictureFilePath != "" && m_originalPictureFilePath != ""){
        auto animation = cocos2d::Animation::create();
        animation->addSpriteFrameWithFile(m_blinkPictureFilePath);
        animation->addSpriteFrameWithFile(m_originalPictureFilePath);
        animation->setDelayPerUnit(0.5);
        auto blinkAction = cocos2d::RepeatForever::create(cocos2d::Animate::create(animation));
        blinkAction->setTag(MAOREBlinkActionTag);
        getMapImageObject()->runAction(blinkAction);
    }
    if(m_hintMapImageObject != nullptr){
        m_hintMapImageObject->runAction(cocos2d::FadeIn::create(AniMiscUtils::StandardAnimationTime));
    }
}

void MapAdventureObjectRotatingEnd::stopBlinking(){
    getMapImageObject()->stopActionByTag(MAOREBlinkActionTag);
    getMapImageObject()->setTexture(m_originalPictureFilePath);
    if(m_hintMapImageObject != nullptr){
        m_hintMapImageObject->runAction(cocos2d::FadeOut::create(AniMiscUtils::StandardAnimationTime));
    }
}


// ensure rotation is between original rotation and target rotation
float MapAdventureObjectRotatingEnd::ensureValidRotation(float rotation, float prevRotation){
    if(!m_onlyAllowRightDirection){
        return rotation;
    }
    
    if(abs(rotation - prevRotation) > 300){
        return prevRotation;
    } // we want to eliminate the 0-359 "jump"
    
    auto newRotation = rotation;
    if(m_readyRotationDegrees > m_originalRotation){
        newRotation = MIN(newRotation, m_readyRotationDegrees);
        newRotation = MAX(newRotation, m_originalRotation);
    } else {
        newRotation = MAX(newRotation, m_readyRotationDegrees);
        newRotation = MIN(newRotation, m_originalRotation);
    }
    return newRotation;
}

void MapAdventureObjectRotatingEnd::reset(){
    if(!m_wasAlwaysReady){
        stopBlinking();
        resetRotation();
        m_isReady = false;
        getMapImageObject()->setMergeWithBackground(false);
        if(m_delegate != nullptr){
            m_delegate->onOccupiedTilesDataChanged(this);
            m_delegate->onBlockedTilesDataChanged(this, std::vector<AniMapUtils::TileData>(), m_tempBlockedTiles);
        }
    }
}