// // AniMapLayer.cpp // SteveMaggieCpp // // Created by Katarzyna Kalinowska-Górska on 17.05.2017. // // #include #include "AniMapLayer.h" #include "AniJSONParseUtils.h" #include "AniGeometryUtils.h" #include "AniMathUtils.h" #include "AniResourceUtilities.h" #include "AniScalingUtils.h" static int MapChildTag = 10; AniMapLayer* AniMapLayer::create(const std::string& resFolder, const std::string& mapPath, const rapidjson::Value* objects) { AniMapLayer * layer = new (std::nothrow) AniMapLayer(); if(layer && layer->init(resFolder, mapPath, objects)) { layer->autorelease(); return layer; } CC_SAFE_DELETE(layer); return nullptr; } bool AniMapLayer::init(const std::string& resFolder, const std::string& mapPath, const rapidjson::Value* objects) { if(!cocos2d::LayerColor::initWithColor(cocos2d::Color4B(0,0,0,255))) { return false; } _resFolder = resFolder; auto scale = /*AniScalingUtils::scaleAspectFillToDesignIpadProSize(); //*/1/cocos2d::Director::getInstance()->getContentScaleFactor(); auto map = cocos2d::TMXTiledMap::create(_resFolder + mapPath); // cocos2d::log("map path: %s\n", (_resFolder + mapPath).c_str()); map->setTileSize(map->getTileSize()*scale); map->setTag(MapChildTag); this->addChild(map, 0); if(objects){ this->addObjectLayers(map, objects); } //TODO SEPARATE FUNCTION SIZE LAYER maybe not necessary at all auto width = map->getMapSize().width*map->getTileSize().width; auto height = map->getMapSize().height*map->getTileSize().height; this->setContentSize(cocos2d::Size(width, height)); // this->setAnchorPoint(cocos2d::Point(0,0)); //// this->setPosition(cocos2d::Point(0,0)); /// return true; } //watch out not to call setup twice without cleanup!! or add cleanup code void AniMapLayer::setup(IMapLayerDataSource* p_dataSource){ auto map = getChildByTag(MapChildTag); m_dataSource = p_dataSource; //TODO throw out if unnecessary m_mapControl = p_dataSource->initAllocMapControl(); // m_mapControlCoordinator = p_dataSource->initAllocMapControlCoordinator(); m_adventureObjectMapper = p_dataSource->initAllocAdventureObjectMapper(); m_mapController = p_dataSource->initAllocMapController(map, m_adventureObjectMapper); ///TEMP for(auto it = m_mapImageObjects.begin(); it != m_mapImageObjects.end(); ++it){ m_mapController->insertMapImageObject(it->first, it->second); } //// m_mapCharacters = p_dataSource->createAutoreleasedMapCharacters(); m_mapCharacterControllers = p_dataSource->initAllocMapCharacterControllers(); attachCharacterControllers(); attachMapControllerToCharacterControllers(m_mapController); auto mapProps = map->getProperties(); if(mapProps.find("adventureObjectsFile") != mapProps.end()){ // cocos2d::log("adv objects file path: %s\n", (_resFolder + mapProps["adventureObjectsFile"].asString()).c_str()); m_mapController->parseAdventureObjects(_resFolder + mapProps["adventureObjectsFile"].asString(), this); } if(mapProps.find("addDarkness") != mapProps.end()){ if(mapProps["addDarkness"].asBool() == true){ auto alpha = 0.8f; //todo magic number default if(mapProps.find("darknessAlpha") != mapProps.end()){ alpha = mapProps["darknessAlpha"].asFloat(); } setupDarkness(alpha); } } //already called in parseAdventureObjects; TODO: cleanup, make more clear // AniMapUtils::getInstance().recalculateMapGraph(m_mapController->getMap(), m_mapController->getTileLayer()); AniMapUtils::getInstance().setMaxTileRangeOnPath(20);//TODO magic number } void AniMapLayer::setupDarkness(float p_alpha){ } void AniMapLayer::reallocMapControl(){ if(m_mapControl != nullptr){ m_mapControl->uninstall(); } deleteMapControlObjects(); m_mapControl = m_dataSource->initAllocMapControl(); } void AniMapLayer::installMapControl(IMapControl* p_newMapControl){ if(p_newMapControl != nullptr){ std::vector controlInterfaces; // todo later here maybe customize , give priority?) coordinator? priority functions? for(auto it = m_mapCharacterControllers.begin(); it != m_mapCharacterControllers.end(); ++it){ controlInterfaces.push_back(*it); } controlInterfaces.push_back(m_mapController); m_mapControl->install(this, m_playerCharacter, controlInterfaces); for(int i = 0; i < m_mapCharacterControllers.size(); ++i){ m_mapCharacterControllers[i]->onEnter(); } } } AniMapLayer::~AniMapLayer(){ deleteMapControlObjects(); deleteMapAdventureObjectMapper(); deleteMapCharacterControllers(); deleteMap(); } void AniMapLayer::onEnter() { cocos2d::Layer::onEnter(); // scheduleUpdate(); // installMapControl(m_mapControl); for(int i = 0; i < m_mapCharacterControllers.size(); ++i){ m_mapCharacterControllers[i]->onEnter(); } } void AniMapLayer::onExit() { cocos2d::Layer::onExit(); this->unscheduleUpdate(); } void AniMapLayer::deleteMap(){ m_mapController->clearAdventureObjects(); m_mapController->getMap()->removeFromParent(); //unnecessary? if(m_mapController != nullptr){ delete m_mapController; m_mapController = nullptr; } } void AniMapLayer::deleteMapControlObjects(){ if(m_mapControl != nullptr){ delete m_mapControl; m_mapControl = nullptr; } // if(m_mapControlCoordinator != nullptr){ // delete m_mapControlCoordinator; // m_mapControlCoordinator = nullptr; // } } void AniMapLayer::deleteMapAdventureObjectMapper(){ if(m_adventureObjectMapper != nullptr){ delete m_adventureObjectMapper; m_adventureObjectMapper = nullptr; } } void AniMapLayer::deleteMapCharacterControllers(){ for(int i = 0; i < m_mapCharacterControllers.size(); ++i){ delete m_mapCharacterControllers[i]; } m_mapCharacterControllers.clear(); } void AniMapLayer::attachCharacterControllers(){ for(int i = 0; i < m_mapCharacters.size(); ++i){ if(m_mapCharacterControllers.size() > i){ // m_mapCharacters[i]->setController(m_mapCharacterControllers[i]); m_mapCharacterControllers[i]->attachToCharacter(m_mapCharacters[i]); } } if(m_mapCharacters.size() > 0){ m_playerCharacter = m_mapCharacters[0]; //TODO _objectLayers[_objectLayers.size() - 1]->addChild(m_playerCharacter, _defaultCharacterDepth); _allObjects.insert({"SteveCharacter", m_playerCharacter}); //TODO AniSteveCharacter - id- should be given by the delegate } } void AniMapLayer::attachMapControllerToCharacterControllers(IMapController* p_mapController){ for(int i = 0; i < m_mapCharacterControllers.size(); ++i){ m_mapCharacterControllers[i]->setMapController(p_mapController); } } cocos2d::Layer* AniMapLayer::addLayer(cocos2d::TMXTiledMap* p_parentMap) { auto ts = p_parentMap->getTileSize(); auto newLayer = cocos2d::Layer::create(); auto width = p_parentMap->getMapSize().width*p_parentMap->getTileSize().width; auto height = p_parentMap->getMapSize().height*p_parentMap->getTileSize().height; newLayer->setContentSize(cocos2d::Size(width, height)); // cocos2d::log("SETUP content size width: %f\n", newLayer->getContentSize().width); newLayer->setAnchorPoint(cocos2d::Point(0,0)); newLayer->setPosition(cocos2d::Point(0,0)); return newLayer; } void AniMapLayer::addObjectLayers(cocos2d::TMXTiledMap* p_parentMap, const rapidjson::Value* objectsData) { auto objectGroups = p_parentMap->getObjectGroups(); for(int i = 0; i < objectGroups.size(); ++i){ auto newObjectLayer = this->addLayer(p_parentMap); _objectLayers.push_back(newObjectLayer); // this->addChild(newObjectLayer); newObjectLayer->setLocalZOrder(20 + i); p_parentMap->addChild(newObjectLayer); } if(objectsData != NULL && (*objectsData).IsArray()){ this->fillObjects(p_parentMap, objectsData); } auto darknessLayer = p_parentMap->getLayer("TileLayerDarkness"); if(darknessLayer != nullptr){ darknessLayer->setLocalZOrder(50); // darknessLayer->setCascadeOpacityEnabled(true); // darknessLayer->setOpacity(200); } } void AniMapLayer::fillObjects(cocos2d::TMXTiledMap* p_parentMap, const rapidjson::Value* externalObjectsData) { for(auto& externalObjectData : externalObjectsData->GetArray()){ auto objectGroup = p_parentMap->getObjectGroup(externalObjectData["objectGroupName"].GetString()); if(objectGroup == NULL){ continue; } auto object = objectGroup->getObject(externalObjectData["objectNameOnMap"].GetString()); std::string objectName = externalObjectData["objectNameOnMap"].GetString(); // if(AniJSONParseUtils::hasMemberString(externalObjectData, "name")){ // objectName = externalObjectData["name"].GetString(); // } else { // objectName = externalObjectData["objectNameOnMap"].GetString(); // } std::string imagePath = _resFolder + externalObjectData["imagePath"].GetString(); // auto newMapObject = MapObjectMapper::createObjectFromClassName(objectType, this, imagePath, objectName, activatable); auto newMapObject = IMapImageObject::create(imagePath, objectName, p_parentMap); const Value* properties = NULL; if(externalObjectData.HasMember("properties")){ properties = &externalObjectData["properties"]; } int depth = 0; if(object.find("depth") != object.end()){ depth = object["depth"].asInt(); } if(newMapObject != nullptr){ if(externalObjectData.HasMember("parentName")){ //TODO: to be sure it would be good to sort by ids or something to ensure the parent is already there std::string parentName = externalObjectData["parentName"].GetString(); if(_allObjects.find(parentName) != _allObjects.end()){ auto parent = _allObjects.at(parentName); parent->addChild(newMapObject, depth); } } else if(objectGroup->getGroupName() == "background_object_layer"){ _objectLayers[0]->addChild(newMapObject, depth); } else { _objectLayers[_objectLayers.size()-1]->addChild(newMapObject, depth); //in practice there are only 2 layers } //temp // newMapObject->setOpacity(100); newMapObject->parseProperties(&object, properties); // these positions assume anchor point = {0.5, 0.5} auto positionX = object["x"].asFloat();// - _cutLeft; auto positionY = object["y"].asFloat();// - _cutTop; auto width = object["width"].asFloat(); auto height = object["height"].asFloat(); // auto exactScale = object.find("exactScale") == object.end() ? false : object["exactScale"].asBool(); // if(exactScale){ // newMapObject->setScale(width/newMapObject->getTexture()->getContentSize().width, height/newMapObject->getTexture()->getContentSize().height); // } else { newMapObject->setScale(width/newMapObject->getTexture()->getContentSize().width); // cocos2d::log("new object name: %s scale: %f\n", newMapObject->getObjectName().c_str(), newMapObject->getScale()); // why doesn't object name get filled everywhere? // } newMapObject->setPosition(positionX + newMapObject->getAnchorPoint().x*width, positionY+newMapObject->getAnchorPoint().y*height); if(externalObjectData.HasMember("anchorPoint")){ auto newAnchorPoint = AniJSONParseUtils::getPoint(externalObjectData["anchorPoint"]); // when we change the anchor point, we also need to adjust the postion so the sprite stays at the same spot auto anchorPointChange = newAnchorPoint - newMapObject->getAnchorPoint(); newMapObject->setAnchorPoint(newAnchorPoint); newMapObject->setPositionX(newMapObject->getPositionX() - anchorPointChange.x*newMapObject->getBoundingBox().size.width); newMapObject->setPositionY(newMapObject->getPositionY() + anchorPointChange.y*newMapObject->getBoundingBox().size.height); } if(externalObjectData.HasMember("rotation")){ newMapObject->setRotation(externalObjectData["rotation"].GetFloat()); } if(externalObjectData.HasMember("flippedX")){ newMapObject->setFlippedX(externalObjectData["flippedX"].GetBool()); } m_mapImageObjects.insert({objectName, newMapObject}); _allObjects.insert({objectName, newMapObject}); } } } void AniMapLayer::addNewObject(IMapImageObject* m_mapImageObject){ _objectLayers[_objectLayers.size()-1]->addChild(m_mapImageObject); m_mapImageObjects.insert({m_mapImageObject->objectName, m_mapImageObject}); _allObjects.insert({m_mapImageObject->objectName, m_mapImageObject}); } void AniMapLayer::setChooseRandomSpawnTile(bool chooseRandom) { _chooseRandomSpawnTile = chooseRandom; } void AniMapLayer::setSpawnTile(int col, int row){ m_spawnTile = AniMapUtils::TileData(col, row); } void AniMapLayer::spawnCharacters() { auto map = m_mapController->getMap(); auto mapProperties = map->getProperties(); auto tile = DefaultSpawnTile; cocos2d::Point spawnLocation; if(_chooseRandomSpawnTile){ do { spawnLocation = AniMapUtils::getInstance().getRandomFreeTile(tile, map, m_mapController->getTileLayer()); } while (m_mapController->getAdventureObjectsAtTile(tile).size() > 0 || m_mapController->getTileObjectLayer()->getTileGIDAt(tile.convertToVec2()) > 0); } else { spawnLocation = AniMapUtils::getInstance().getTileMiddlePosition(m_mapController->getMap(), m_spawnTile.col, m_spawnTile.row); } _defaultCharacterDepth = 10; if(mapProperties.find("character_depth") != mapProperties.end()){ _defaultCharacterDepth = mapProperties["character_depth"].asInt(); } // m_playerCharacter->setPosition(spawnLocation.x, getContentSize().height - m_playerCharacter->getBoundingBox().size.height); m_mapController->setCameraVelocity(2000); m_playerCharacter->fallTo(spawnLocation, 0, [&](){ m_mapController->centerCameraOnNode(m_playerCharacter); m_mapController->setCameraVelocity(IMapController::DefaultCameraVelocity); // m_mapController->setCameraCenter(m_playerCharacter->getPosition()); // this->adjustSteveDepth(); if(m_delegate){ m_delegate->onCharacterFinishedIntroFalling(m_playerCharacter); } // m_playerCharacter->temporarilyChangeClothes(10); }); m_mapController->centerCameraOnNode(m_playerCharacter, true); // m_mapController->setCameraCenter(m_playerCharacter->getPosition(), true); //todo method camera jump to destination? // this->adjustSteveDepth(); scheduleUpdate(); } bool AniMapLayer::fastForwardCharacterFall(){ if(m_playerCharacter->getCharacterState() == IMapCharacter::FALLING){ m_playerCharacter->fastForwardFall(); m_mapController->centerCameraOnNode(m_playerCharacter, true); m_mapCharacterControllers.at(0)->instantTilesReveal(true); return true; } return false; } void AniMapLayer::clearCharacters(){ // if(m_playerCharacter != nullptr){ // m_playerCharacter->removeFromParent(); // m_playerCharacter = nullptr; // } } void AniMapLayer::update(float dt) { for(auto characterIt = m_mapCharacters.begin(); characterIt != m_mapCharacters.end(); ++characterIt){ (*characterIt)->update(dt); (*characterIt)->getController()->update(dt); } m_mapController->update(dt); } void AniMapLayer::onMapAdventureObjectEvent(IMapAdventureObject* p_object, IMapAdventureObjectEvent* event){ if(m_delegate){ m_delegate->onMapAdventureObjectEvent(p_object, event); } } void AniMapLayer::onOccupiedTilesDataChanged(IMapAdventureObject* p_object) { m_mapController->remapAdventureObject(p_object); } void AniMapLayer::onBlockedTilesDataChanged(IMapAdventureObject* p_object, std::vector p_oldBlockedTiles, std::vector p_newBlockedTiles) { m_mapController->removeBlockedTiles(p_oldBlockedTiles, true); m_mapController->addBlockedTiles(p_newBlockedTiles, true); }