// Ryzom - MMORPG Framework // Copyright (C) 2010 Winch Gate Property Limited // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . ///////////// // INCLUDE // ///////////// #include "stdpch.h" // Misc #include "nel/misc/vectord.h" #include "nel/misc/path.h" #include "nel/misc/smart_ptr.h" #include "nel/misc/progress_callback.h" #include "nel/misc/async_file_manager.h" #include "nel/misc/bitmap.h" #include "nel/misc/polygon.h" // 3D Interface. #include "nel/3d/u_landscape.h" #include "nel/3d/u_scene.h" #include "nel/3d/u_driver.h" #include "nel/3d/u_texture.h" // Georges #include "nel/georges/u_form.h" #include "nel/georges/u_form_elm.h" #include "nel/georges/u_form_loader.h" // Client #include "global.h" #include "continent.h" #include "light_cycle_manager.h" // \todo GUIGUI : maybe change this when the time will be better managed. #include "door_manager.h" #include "client_cfg.h" #include "ig_client.h" #include "pacs_client.h" #include "village.h" #include "ig_callback.h" #include "streamable_ig.h" #include "weather_manager_client.h" #include "weather.h" #include "entities.h" #include "fog_map.h" #include "demo.h" #include "ingame_database_manager.h" #include "sky_render.h" #include "fix_season_data.h" #include "ig_season_callback.h" #include "user_entity.h" #include "micro_life_manager.h" #include "zone_util.h" #include "mesh_camera_col_manager.h" #include "misc.h" #include "sheet_manager.h" #include "continent_manager.h" #include "connection.h" #include "water_env_map_rdr.h" #include "input.h" #include "bg_downloader_access.h" // Game Share #include "game_share/georges_helper.h" #include "game_share/season_file_ext.h" // R2 Share #include "game_share/scenario_entry_points.h" #include "r2/editor.h" // std #include #include #include #include /////////// // USING // /////////// using namespace NLMISC; using namespace NL3D; using namespace std; using namespace NLGEORGES; //////////// // EXTERN // //////////// extern ULandscape *Landscape; extern UScene *Scene; extern UScene *SceneRoot; extern UInstanceGroup *BackgroundIG; extern UDriver *Driver; extern UVisualCollisionManager *CollisionManager; extern bool InitCloudScape; extern bool FirstFrame; extern CContinentManager ContinentMngr; //----------------------------------------------- // CUserLandMark //----------------------------------------------- NLMISC::CRGBA CUserLandMark::getColor () const { return _LandMarksColor[Type]; } //----------------------------------------------- // CFogOfWar //----------------------------------------------- //=================================================================================== CFogOfWar::~CFogOfWar() { if (Tx != NULL) Driver->deleteTextureMem(Tx); } //=================================================================================== uint8 *CFogOfWar::getData() { return (Tx != NULL) ? Tx->getPointer() : NULL; } //=================================================================================== sint16 CFogOfWar::getRealWidth() { return (Tx != NULL) ? (sint16)Tx->getImageWidth() : 0; } //=================================================================================== sint16 CFogOfWar::getRealHeight() { return (Tx != NULL) ? (sint16)Tx->getImageHeight() : 0; } //=================================================================================== bool CFogOfWar::createData(sint16 w, sint16 h) { if (Tx != NULL) Driver->deleteTextureMem(Tx); MapWidth = w; MapHeight = h; uint32 WReal = NLMISC::raiseToNextPowerOf2(MapWidth); uint32 HReal = NLMISC::raiseToNextPowerOf2(MapHeight); Tx = Driver->createTextureMem(WReal, HReal, CBitmap::Alpha); if (Tx == NULL) return false; Tx->setWrapS(NL3D::UTexture::Clamp); Tx->setWrapT(NL3D::UTexture::Clamp); uint8 *pData = Tx->getPointer(); // Set the texture to black (not discovered) for (uint32 j = 0; j < WReal*HReal; ++j) pData[j] = 0; // Upload it Tx->touch(); return true; } //=================================================================================== void CFogOfWar::explored(sint16 /* mapPosX */, sint16 /* mapPosY */) { // TODO trap : do something better than upload the whole texture if (Tx != NULL) Tx->touch(); } //=================================================================================== void CFogOfWar::load(const std::string &/* contName */) { /* // Temporary comment : see this when server ready string fileName = "save/fow_" + contName + "_" + PlayerSelectedFileName + ".ibmp"; // Try to load the file corresponding CIFile inFile; if (inFile.open(fileName)) { serial(inFile); } else { // If the file do not exist create a new texture // Look in all the maps to know the texture map size and apply a ratio to this size CWorldSheet *pWS = dynamic_cast(SheetMngr.get(CSheetId("ryzom.world"))); if (pWS == NULL) return; for (uint32 i = 0; i < pWS->Maps.size(); ++i) { SMap &rM = pWS->Maps[i]; if (strncmp(rM.Name.c_str(), "continent", 9) == 0) { if (stricmp(rM.ContinentName.c_str(), contName.c_str()) == 0) { // get the size of the bitmap uint32 nWidth = 0, nHeight = 0; string path = CPath::lookup(rM.BitmapName, false); if (!path.empty()) CBitmap::loadSize(path, nWidth, nHeight); // create the texture fow if ((nWidth != 0) && (nHeight != 0)) { MapWidth = (sint16)nWidth / 4; MapHeight = (sint16)nHeight / 4; createData(MapWidth, MapHeight); } MinX = rM.MinX; MinY = rM.MinY; MaxX = rM.MaxX; MaxY = rM.MaxY; return; } } } } */ } //=================================================================================== void CFogOfWar::save(const std::string &/* contName */) { /* // Temporary comment : see this when server ready string fileName = "save/fow_" + contName + "_" + PlayerSelectedFileName + ".ibmp"; // Try to load the file corresponding COFile inFile; if (inFile.open(fileName)) serial(inFile); */ } ///////////// // METHODS // ///////////// //----------------------------------------------- // CContinent : // Constructor. //----------------------------------------------- CContinent::CContinent() { // Newbie = false; }// CContinent // class CChangeTaskPriority : public CTaskManager::IChangeTaskPriority { virtual float getTaskPriority(const IRunnable &runnable) { const NLMISC::IRunnablePos *_3dRunnable = dynamic_cast(&runnable); if (_3dRunnable) { return (_3dRunnable->Position - UserEntity->pos()).norm(); } return 0; } } ChangeTaskPriority; //=================================================================================== void CContinent::setup() { if (SheetName.empty()) { nlwarning("SheetName not set : cannot setup continent"); return; } CEntitySheet *pES = SheetMngr.get(CSheetId (SheetName)); CContinentSheet *pCS = NULL; if (pES->type() == CEntitySheet::CONTINENT) pCS = (CContinentSheet*)pES; if (pCS == NULL) { nlwarning("Bad type for continent form %s.", SheetName.c_str()); return; } // copy basics parameters of sheet CContinentParameters::operator = (pCS->Continent); // setup weather functions for(uint k = 0; k < EGSPD::CSeason::Invalid; ++k) { WeatherFunction[k].buildFromSheet(pCS->WeatherFunction[k], WeatherManager); } /* nlinfo("Seasons for continent %s", SheetName.c_str()); nlinfo("=================================="); for(uint k = 0; k < EGSPD::CSeason::Invalid; ++k) { nlinfo("Season = %s, num setups = %d", EGSPD::CSeason::toString((EGSPD::CSeason::TSeason) k).c_str(), (int) WeatherFunction[k].getNumWeatherSetups()); for(uint l = 0; l < WeatherFunction[k].getNumWeatherSetups(); ++l) { if (WeatherFunction[k].getWeatherSetup(l)) { nlinfo("Setup %d for season %s is %s", (int) l, EGSPD::CSeason::toString((EGSPD::CSeason::TSeason) k).c_str(), NLMISC::CStringMapper::unmap(WeatherFunction[k].getWeatherSetup(l)->SetupName).c_str()); } else { nlinfo("Setup %d not found", (int) l); } } } */ uint k; // setup villages for(k = 0; k < pCS->Villages.size(); ++k) { CVillage *village = new CVillage; if (village->setupFromSheet(Scene, pCS->Villages[k], &IGLoaded)) _Villages.add(village); else delete village; } // Setup outpost _Outposts.clear(); for(k = 0; k < pCS->Continent.ZCList.size(); ++k) { // must add the ruins in 3D? bool mustAddRuins= pCS->Continent.ZCList[k].EnableRuins; // add a village for ruins display? CVillage *village= NULL; if(mustAddRuins) { village = new CVillage; if (village->setupOutpost(Scene, pCS->Continent.ZCList[k], k, &IGLoaded)) _Villages.add(village); else { delete village; village= NULL; } } // add an outpost COutpost outpost; if (outpost.setupOutpost(pCS->Continent.ZCList[k], k, village)) _Outposts.push_back(outpost); } // setup fog of war FoW.load(Name); } //----------------------------------------------- class CPCBank : public IProgressCallback { virtual void progress (float /* progressValue */) {} }; CPCBank PCBank; static uint getNumZones() { if (Landscape == NULL) return 0; std::vector zoneLoaded; Landscape->getAllZoneLoaded(zoneLoaded); return (uint)zoneLoaded.size(); } //----------------------------------------------- // select : // Update global parameters like the texture for the micro veget. //----------------------------------------------- void CContinent::select(const CVectorD &pos, NLMISC::IProgressCallback &progress, bool complete, bool unhibernate, EGSPD::CSeason::TSeason season) { pauseBGDownloader(); CNiceInputAuto niceInputs; // Season has changed ? Season = season; if (Landscape) Landscape->setTileColor(TileColorMono[Season], TileColorFactor[Season]); // First frame FirstFrame = true; { H_AUTO(InitRZWorldLanscapeShow) // If continent is an indoor, hde the landscape if (Landscape) { if (Indoor) Landscape->hide(); else Landscape->show(); } } bool toto = false; if(complete) { // Progress bar progress.progress (0); progress.pushCropedValues (0, 1.f/3.f); { H_AUTO(InitRZWorldIndoor) if (!ClientCfg.Light) { if (Landscape) { Landscape->removeAllZones(); toto= true; // If continent is an indoor, hde the landscape if (!Indoor) { // Change the MicroVeget Texture. string microVegetSeason = MicroVeget; SeasonPostfixTextureFilename (microVegetSeason, Season); Landscape->loadVegetableTexture (microVegetSeason); // Change the Material for static and dynamic PointLights Landscape->setPointLightDiffuseMaterial(LandscapePointLightMaterial); } } } } // Initialize Landscape IG Callbacks. { H_AUTO(InitRZWorldInitCallbacks) initLandscapeIGCallbacks(); nlassert(IGCallbacks); // forgot to call ::initLandscapeIGCallbacks() if (!ClientCfg.Light) { // Add weather as an observer (to know when an ig is added, and to set its lightmaps color accordingly) registerObserver(&LightCycleManager); IGCallbacks->registerObserver(&LightCycleManager); // register the LightCycleManager as an observer to Objects that may create IGs IGSeasonCallback.Season = Season; registerObserver(&IGSeasonCallback); IGCallbacks->registerObserver(&IGSeasonCallback); // register the Camera collision manager registerObserver(&MeshCameraColManager); if (!Indoor) { // Change LOD texture try { // Get the CoarseMeshMap for this season string seasonname = CoarseMeshMap; SeasonPostfixTextureFilename (seasonname, Season); // Set the texture for the coarse mesh manager Scene->setCoarseMeshManagerTexture (CPath::lookup(seasonname).c_str()); } catch (Exception &e) { nlwarning (e.what()); } } } // Register door handling (when an ig is added check all shapes for doors and add to the door manager) registerObserver(&IGDoorCallback); } { H_AUTO(InitRZWorldPacs) releasePACS(); // Init PACS std::string pacsRBankPath = CPath::lookup(PacsRBank, false); std::string pacsGRPath = CPath::lookup(PacsGR, false); if(!pacsRBankPath.empty() && !pacsGRPath.empty()) initPACS(pacsRBankPath.c_str(), pacsGRPath.c_str(), progress); else initPACS(0, 0, progress); // Set the move container if (IGCallbacks) IGCallbacks->setMoveContainer(PACS); } { H_AUTO(InitRZWorldBuildings) // No landscape IG in indoors if (!Indoor) { if (!ClientCfg.Light && Landscape) { // Init ig try { LandscapeIGManager.initIG (Scene, CPath::lookup(LandscapeIG).c_str(), Driver, Season, &progress); } catch (Exception &e) { nlwarning (e.what()); } } // Get IGs for the continent with their name. vector > igsWithNames; LandscapeIGManager.getAllIGWithNames(igsWithNames); // Associate IGs with the ZC number or -1 if there is no ZC. for(uint i = 0; i this zone should be a ZC. string outpostZone = strlwr(CFile::getFilenameWithoutExtension(ZCList[j].Name)); if(igZone == outpostZone) { nlctassert(RZ_MAX_BUILDING_PER_OUTPOST==4); // Check there is all informations in the IG for a ZC and initialize. bool zcOK[RZ_MAX_BUILDING_PER_OUTPOST+1] = {false, false, false, false, false}; UInstanceGroup *ig = igsWithNames[i].first; uint k; for(k=0; kgetNumInstance(); ++k) { if(ig->getInstanceName(k) == "flag_zc") { // todo hulud handle outpost flag zcOK[0] = true; } // For each building uint b; for (b=0; bgetInstanceName(k) == ("bat_zc_0"+toString(b+1))) { // Set the position CVector pos = ig->getInstancePos(k); CQuat rot = ig->getInstanceRot(k); outpost->setBuildingPosition (b, rot, pos); zcOK[b+1] = true; } } } // There are not enough informations in the IG for ZC. for (k=0; kaddIG(igsWithNames[i].first); } // Register outpost collisions and Display initOutpost(); } } if (!ClientCfg.Light) { // Progress bar progress.popCropedValues (); progress.progress (1.f/3.f); progress.pushCropedValues (1.f/3.f, 2.f/3.f); { H_AUTO(InitRZWorldTileBank) // Select the tile bank if (Landscape) { if (!Indoor) { string farBankSeason = FarBank; SeasonPostfixTextureFilename (farBankSeason, Season); if (!SmallBank.empty() && !farBankSeason.empty()) { std::string smallBankPath = CPath::lookup(SmallBank, false); std::string farBankSeasonPath = CPath::lookup(farBankSeason, false); if (!smallBankPath.empty() && !farBankSeasonPath.empty()) { Landscape->loadBankFiles (smallBankPath, farBankSeasonPath); } // Postfix tiles and vegetset by the season string Landscape->postfixTileFilename (CSeasonFileExt::getExtension (Season)); Landscape->postfixTileVegetableDesc (CSeasonFileExt::getExtension (Season)); // Flush the tiles Landscape->flushTiles (progress); } } } } // Not an indoor ? if (!Indoor) { { H_AUTO(InitRZWorldSky) // load the sky if any initSky(); // build the canopee try { nlassert(CurrSeason < EGSPD::CSeason::Invalid); if (!CanopyIGfileName[CurrSeason].empty() && CurrSeason != EGSPD::CSeason::Invalid) { BackgroundIG = UInstanceGroup::createInstanceGroup(CanopyIGfileName[CurrSeason]); } else { BackgroundIG = UInstanceGroup::createInstanceGroup(BackgroundIGName); } } catch (Exception &e) { nlwarning (e.what()); } if(BackgroundIG) { if (SceneRoot) { // Add the IG to the scene BackgroundIG->addToScene(*SceneRoot, Driver); // See for which objects distance should be overriden (object which use a .PLANT sheet) uint numInstances = BackgroundIG->getNumInstance(); uint k; for(k = 0; k < numInstances; ++k) { // Select the texture function of the season UInstance instance = BackgroundIG->getInstance (k); if (!instance.empty()) { instance.selectTextureSet (Season); } } } } } } } // Progress bar progress.popCropedValues (); progress.progress (2.f/3.f); progress.pushCropedValues (2.f/3.f, 1); }// complete if (complete || unhibernate) { H_AUTO(InitRZWorldLightCycleManager) // Touch light setup LightCycleManager.touch(); } if (complete) { H_AUTO(InitRZWorldWaterMap) // init water map initWaterMap(); } const R2::CScenarioEntryPoints::CCompleteIsland *completeIsland = NULL; { progress.pushCropedValues (0, 3.f/4.f); if (!ClientCfg.Light) { if (!Indoor) { if (Landscape) { H_AUTO(InitRZWorldLandscapeIGManager) // If any zone already loaded into the landscape, must load them to the IGManager. // Yoyo: this is important for teleportation near a current position, since not so many landscape zones // will be removed/added. // Hence, a solution is to reload all igs. vector zonesLoaded; Landscape->getAllZoneLoaded(zonesLoaded); // and try to load the ones which fit the IGManager vector igAdded; LandscapeIGManager.loadArrayZoneIG(zonesLoaded, &igAdded); // Refresh zone to load/remove. vector zonesAdded; vector zonesRemoved; completeIsland = R2::CScenarioEntryPoints::getInstance().getCompleteIslandFromCoords(CVector2f((float) UserEntity->pos().x, (float) UserEntity->pos().y)); Landscape->refreshAllZonesAround(pos, ClientCfg.Vision + ExtraZoneLoadingVision, zonesAdded, zonesRemoved, progress, completeIsland ? &(completeIsland->ZoneIDs) : NULL); LandscapeIGManager.unloadArrayZoneIG(zonesRemoved); LandscapeIGManager.loadArrayZoneIG(zonesAdded, &igAdded); } } } progress.popCropedValues (); progress.progress (3.f/4.f); progress.pushCropedValues (3.f/4.f, 1); // Refresh PACS if(GR) { H_AUTO(InitRZWorldRefreshPacs) GR->refreshLrAroundNow(pos, LRRefeshRadius); } // Refresh streamable object (villages ..) if (!ClientCfg.Light) { if (ClientCfg.VillagesEnabled) { H_AUTO(InitRZWorldForceUpdateStreamable) forceUpdateStreamable(pos, progress); } } progress.popCropedValues (); } // Execute only when you change the targeted continent is not the current one. if(complete || unhibernate) { // Continent changed -> update entities. EntitiesMngr.changeContinent(); R2::getEditor().onContinentChanged(); // Ask to reinit cloudscape InitCloudScape = true; // Set the entity sun contribution parameters CollisionManager->setSunContributionPower (EntitySunContributionPower, EntitySunContributionMaxThreshold); } if(complete) { reloadFogMap(); // temp for debug : create visualisation of current pacs primitives // createInstancesFromMoveContainer(Scene, IGCallbacks->getMoveContainer(), NULL); // // Progress bar progress.popCropedValues (); } if (complete || unhibernate) { if (!ClientCfg.Light && ClientCfg.LandscapeEnabled && ClientCfg.MicroLifeEnabled) { H_AUTO(InitRZWorldLoadMicroLife) loadMicroLife(); } } // Register callback on streaming CAsyncFileManager::getInstance().registerTaskPriorityCallback(&ChangeTaskPriority); }// select // //========================================================================= void CContinent::reloadFogMap() { const R2::CScenarioEntryPoints::CCompleteIsland *completeIsland = R2::CScenarioEntryPoints::getInstance().getCompleteIslandFromCoords(CVector2f((float) UserEntity->pos().x, (float) UserEntity->pos().y)); if (completeIsland) { std::string islandName = strlwr(completeIsland->Island); std::string::size_type lastPos = islandName.find_last_of("_"); if (lastPos != std::string::npos) { islandName = islandName.substr(lastPos + 1); } // special fogmaps are use for islands CFogMapBuild fmb; enum TMapType { Day = 0, Night, Dusk, Distance, Depth, NoPrecipitation, NumMap }; fmb.Map[CFogMapBuild::Day] = "fog_" + islandName + "_day.tga"; fmb.Map[CFogMapBuild::Night] = "fog_" + islandName + "_night.tga"; fmb.Map[CFogMapBuild::Dusk] = "fog_" + islandName + "_day.tga"; fmb.Map[CFogMapBuild::Distance] = "fog_" + islandName + "_dist.tga"; fmb.Map[CFogMapBuild::Depth] = "fog_" + islandName + "_depth.tga"; fmb.Map[CFogMapBuild::NoPrecipitation] = islandName + "_no_rain.tga"; // there is an outtline of 50%, take it in account float deltaX = 0.25f * (completeIsland->XMax - completeIsland->XMin); float deltaY = 0.25f * (completeIsland->XMax - completeIsland->XMin); FogMap.init(fmb, CVector((float) completeIsland->XMin + deltaX, (float) completeIsland->YMin + deltaY, 0.f), CVector((float) completeIsland->XMax - deltaX, (float) completeIsland->YMax - deltaY, 0.f) ); bool found = false; for(uint k = 0; k < CFogMapBuild::NumMap && !found; ++k) { if (FogMap.getMap((CFogMap::TMapType) k).getWidth() != 0) found = true; } // as a fallback, assume that new fog maps have not been installed, so fallback on the old ones ... if (!found) { FogMap.init(FogMapBuild); } } else { FogMap.init(FogMapBuild); } #ifdef NL_DEBUG nlinfo("fog map : (minX, minY) = (%.1f, %.1f), (maxX, maxY) = (%.1f, %.1f)", FogMap.getMinCoord().x, FogMap.getMinCoord().y, FogMap.getMaxCoord().x, FogMap.getMaxCoord().y); #endif } //========================================================================= bool CContinent::getCorners(NLMISC::CVector2f &cornerMin, NLMISC::CVector2f &cornerMax) const { if (!getPosFromZoneName(ZoneMin, cornerMin)) { nlwarning("Can't retrieve continent min corner"); return false; } if (!getPosFromZoneName(ZoneMax, cornerMax)) { nlwarning("Can't retrieve continent max corner"); return false; } if(cornerMin.x > cornerMax.x) std::swap(cornerMin.x, cornerMax.x); if(cornerMin.y > cornerMax.y) std::swap(cornerMin.y, cornerMax.y); cornerMax.x += 160.f; cornerMax.y += 160.f; return true; } //========================================================================= void CContinent::initWaterMap() { NLMISC::CVector2f cornerMin, cornerMax; if (!getCorners(cornerMin, cornerMax)) return; WaterMap.init(cornerMin, cornerMax); } //========================================================================= void CContinent::loadMicroLife() { CMicroLifeManager::getInstance().release(); NLMISC::CVector2f cornerMin, cornerMax; if (!getCorners(cornerMin, cornerMax)) return; CMicroLifeManager::getInstance().init(cornerMin, cornerMax); // register all micro life primitives CMicroLifeManager::getInstance().build(MicroLifeZones); } //========================================================================= bool CContinent::isLoadingforced(const NLMISC::CVector &pos) const { return _Villages.needCompleteLoading(pos); } //========================================================================= void CContinent::updateStreamable(const NLMISC::CVector &pos) { _Villages.update(pos); } //========================================================================= void CContinent::forceUpdateStreamable(const NLMISC::CVector &playerPos, NLMISC::IProgressCallback &progress) { _Villages.forceUpdate(playerPos, progress); } //========================================================================= void CContinent::removeVillages() { _Villages.removeAll(); } //========================================================================= void CContinent::unselect() { // remove envmap if (SceneRoot && !WaterEnvMapCanopyCam.empty()) { SceneRoot->deleteCamera(WaterEnvMapCanopyCam); } WaterEnvMapCanopyCam.detach(); if (CurrentSky.getScene() && !WaterEnvMapSkyCam.empty()) { CurrentSky.getScene()->deleteCamera(WaterEnvMapSkyCam); } WaterEnvMapSkyCam.detach(); // if (!Indoor) { releaseSky(); // Setup the Root scene. if (BackgroundIG) { BackgroundIG->removeFromScene (*SceneRoot); SceneRoot->deleteInstanceGroup (BackgroundIG); BackgroundIG = NULL; } } // Remove outpost (collisions etc...) removeOutpost(); // Unregister callback on streaming CAsyncFileManager::getInstance().registerTaskPriorityCallback(NULL); // Remove the primitive for all entitites (new PACS coming soon and need new primitives). EntitiesMngr.removeCollision(); // release collision primitives if (IGCallbacks) { IGCallbacks->resetContainer(); } // Unload villages _Villages.forceUnload(); // Release PACS releasePACS (); // Initialize Landscape IG Callbacks. releaseLandscapeIGCallbacks(); // Release the IG manager only if where are not in indoor if (!Indoor) { // Release the IG manager if (Landscape) LandscapeIGManager.reset (); } // release fog maps FogMap.release(); // Remove weather as an observer removeObserver(&LightCycleManager); // Remove season observer removeObserver(&IGSeasonCallback); // Remove Camera collision observer removeObserver(&MeshCameraColManager); // Remove door management observers removeObserver(&IGDoorCallback); // Release water map WaterMap.release(); // Release water envmap #ifdef USE_WATER_ENV_MAP Driver->deleteWaterEnvMap(WaterEnvMap); WaterEnvMap = NULL; #endif } //========================================================================= /** Override current fog by the fog in the weather system. This is used when there is bad weather or precipitations * \param dest the fog state to modify * \param fogIndex the fog to modify (0 for main, 1 for canopy) */ static void overrideFog(CFogState &dest, TFogType fogType, float dayNight, float duskRatio, CLightCycleManager::TLightState lightState, bool /* overrideDist */, bool overideByWeatherFogDist = true) { nlassert(fogType < NumFogType); // For now, 2 kinds of fogs : main & canopy if (!dest.FogEnabled) return; const CWeatherState &ws = WeatherManager.getCurrWeatherState(); if (ws.FogRatio == 0.f) return; // no need to override fog // override the fog by the fog indicated in the WeatherManager NLMISC::CRGBA fogColor; // if there's a new style sky with a fog bitmap *(giving fog depending on hour & weather, use it) if (ContinentMngr.cur() && ContinentMngr.cur()->CurrentSky.getScene() != NULL && ContinentMngr.cur()->CurrentSky.hasFogColor()) { fogColor = ContinentMngr.cur()->CurrentSky.computeFogColor(CClientDate(RT.getRyzomDay(), (float) DayNightCycleHour), WeatherManager.getWeatherValue()); } else { switch(lightState) { case CLightCycleManager::DayToNight: { if (dayNight < duskRatio) { float blendFactor = duskRatio != 0 ? dayNight / duskRatio : 0.f; fogColor.blendFromui(ws.FogColorDay, ws.FogColorDusk, (uint) (blendFactor * 256.f)); } else { float blendFactor = duskRatio != 1 ? (dayNight - duskRatio) / (1.f - duskRatio) : 0.f; fogColor.blendFromui(ws.FogColorDusk, ws.FogColorNight, (uint) (blendFactor * 256.f)); } } break; default: // no dusk transition fogColor.blendFromui(ws.FogColorDay, ws.FogColorNight, (uint) (dayNight * 256.f)); break; } } float finalFogAlpha = (float)dest.FogColor.A * 256.f / 255.f; dest.FogColor.blendFromui(dest.FogColor, fogColor, (uint) ((256.f-finalFogAlpha) * ws.FogRatio)); if (overideByWeatherFogDist) { dest.FogEndDist = std::min (ws.FogFar[fogType], dest.FogEndDist); dest.FogStartDist = std::min (ws.FogNear[fogType], dest.FogStartDist); } } //========================================================================= /** Adapt fog dist to the viewport */ static inline void makeRangedFog(CFogState &result, UScene &scene) { if (result.FogEndDist != 0.f) { float farPlaneDist = scene.getCam().getFrustum().Far; float newEndFogDist = std::min(result.FogEndDist, farPlaneDist); result.FogStartDist = result.FogStartDist * newEndFogDist / result.FogEndDist; result.FogEndDist = newEndFogDist; result.FogEnabled = true; } else { result.FogEnabled = false; } } //========================================================================= void CContinent::getFogState(TFogType fogType, float dayNight, float duskRatio, CLightCycleManager::TLightState lightState, const NLMISC::CVectorD &pos, CFogState &result, bool overideByWeatherFogDist /*= true*/) { if (FogStart == FogEnd) { result.FogEnabled = false; return; } switch(fogType) { case MainFog: FogMap.getFogParams(FogStart, FogEnd, (float) pos.x, (float) pos.y, dayNight, duskRatio, lightState, result); break; case CanopyFog: FogMap.getFogParams(RootFogStart, RootFogEnd, (float) pos.x, (float) pos.y, dayNight, duskRatio, lightState, result); break; default: nlstop; break; } // Override fog because of weather. overrideFog(result, fogType, dayNight, duskRatio, lightState, overideByWeatherFogDist); // normalize fog dist to view frustum /* switch(fogType) { case MainFog: if (Scene) makeRangedFog(result, *Scene); break; case CanopyFog: if (SceneRoot) makeRangedFog(result, *SceneRoot); break; } */ /* if (fogType == MainFog) { result.FogStartDist *= (ClientCfg.Vision / ClientCfg.Vision_max); result.FogEndDist *= (ClientCfg.Vision / ClientCfg.Vision_max); } */ } //========================================================================= bool CContinent::enumIGs(IIGEnum *callback) { uint numEntities = _Villages.getNumEntities(); bool continueEnum = true; for(uint k = 0; k < numEntities && continueEnum; ++k) { CVillage *v = dynamic_cast(_Villages.getEntity(k)); if (v) // well this should always be the case, but it may change in the future { continueEnum = v->enumIGs(callback); } } return continueEnum; } //========================================================================= void CContinent::registerObserver(IIGObserver *obs) { uint numEntities = _Villages.getNumEntities(); for(uint k = 0; k < numEntities; ++k) { CVillage *v = dynamic_cast(_Villages.getEntity(k)); if (v) // well this should always be the case, but it may change in the future { v->registerObserver(obs); } } } //========================================================================= void CContinent::removeObserver(IIGObserver *obs) { uint numEntities = _Villages.getNumEntities(); for(uint k = 0; k < numEntities; ++k) { CVillage *v = dynamic_cast(_Villages.getEntity(k)); if (v) // well this should always be the case, but it may change in the future { v->removeObserver(obs); } } } //========================================================================= bool CContinent::isObserver(IIGObserver *obs) const { uint numEntities = _Villages.getNumEntities(); for(uint k = 0; k < numEntities; ++k) { CVillage *v = dynamic_cast(_Villages.getEntity(k)); if (v) // well this should always be the case, but it may change in the future { if (v->isObserver(obs)) return true; } } return false; } //========================================================================= COutpost *CContinent::getOutpost (uint outpostId) { for (uint i=0; i<_Outposts.size(); i++) { // Outpost ? if(_Outposts[i].getOutpostId()==(sint)outpostId) return &_Outposts[i]; } return NULL; } //========================================================================= void CContinent::initOutpost() { for (uint i=0; i<_Outposts.size(); i++) { _Outposts[i].initOutpost(); } } //========================================================================= void CContinent::removeOutpost() { for (uint i=0; i<_Outposts.size(); i++) { _Outposts[i].removeOutpost(); } } //========================================================================= void CContinent::dumpVillagesLoadingZones(const std::string &filename) { const uint NUM_METER_PER_PIX = 4; CBitmap outBitmap; CVector2f minPos, maxPos; getPosFromZoneName(ZoneMin, minPos); getPosFromZoneName(ZoneMax, maxPos); if (minPos.x > maxPos.x) std::swap(minPos.x, maxPos.x); if (minPos.y > maxPos.y) std::swap(minPos.y, maxPos.y); uint width = (uint) ((maxPos.x - minPos.x) / NUM_METER_PER_PIX); uint height = (uint) ((maxPos.y - minPos.y) / NUM_METER_PER_PIX); outBitmap.resize(width, height); for(uint k = 0; k < _Villages.getNumEntities(); ++k) { CVillage &v = *safe_cast(_Villages.getEntity(k)); if(!v.isOutpost()) { const CStreamableIG &sig = v.getIG(); drawDisc(outBitmap, (sig.getPos().x - minPos.x) / NUM_METER_PER_PIX, (sig.getPos().y - minPos.y) / NUM_METER_PER_PIX, sig.getUnloadRadius() / NUM_METER_PER_PIX, CRGBA(0, 0, 100), true); drawDisc(outBitmap, (sig.getPos().x - minPos.x) / NUM_METER_PER_PIX, (sig.getPos().y - minPos.y) / NUM_METER_PER_PIX, sig.getLoadRadius() / NUM_METER_PER_PIX, CRGBA(0, 100, 0), true); drawDisc(outBitmap, (sig.getPos().x - minPos.x) / NUM_METER_PER_PIX, (sig.getPos().y - minPos.y) / NUM_METER_PER_PIX, sig.getForceLoadRadius() / NUM_METER_PER_PIX, CRGBA(100, 0, 0), true); } } // load the map and do a lerp between the 2 bitmaps if (!WorldMap.empty()) { std::string path = CPath::lookup(WorldMap, false); if (!path.empty()) { CIFile stream; if (stream.open(path)) { CBitmap worldMap; if (worldMap.load(stream)) { worldMap.convertToType(CBitmap::RGBA); worldMap.flipV(); worldMap.resample(outBitmap.getWidth(), outBitmap.getHeight()); outBitmap.blend(outBitmap, worldMap, 127, true); } } } } drawDisc(outBitmap, ((float) UserEntity->pos().x - minPos.x) / (float) NUM_METER_PER_PIX, ((float) UserEntity->pos().y - minPos.y) / (float) NUM_METER_PER_PIX, 2, CRGBA::Magenta); // draw grid const uint subdiv= 500; const CRGBA lineCol(CRGBA::Yellow); sint minY= (sint)floor(minPos.y/subdiv); sint maxY= (sint)ceil (maxPos.y/subdiv); sint minX= (sint)floor(minPos.x/subdiv); sint maxX= (sint)ceil (maxPos.x/subdiv); // draw HLines for(sint y= minY;y=0 && ypix<(sint)height) { for(uint x=0;x=0 && xpix<(sint)width) { for(uint y=0;yType == CEntitySheet::SKY) { // new-style sky CurrentSky.init(Driver, *static_cast(skySheet), false, WorldLightCycle.NumHours); } #ifdef USE_WATER_ENV_MAP WaterEnvMapRdr.Sky = &CurrentSky; #endif } else { createSkyScene(); // fallback to previous sky version Sky = SkyScene->createInstance(SkyDay); Sky2ndPass = SkyScene->createInstance(SkyDay); // Sky shape used for second pass. We use it to keep pointers on textures SkyFogPart = SkyScene->createInstance(SkyFogPartName); // Setup the 2nd if (!Sky.empty()) { DaySkySetup.buildFromInstance(Sky, 0); // day textures are at stage 0 NightSkySetup.buildFromInstance(Sky, 1); // night textures are at stage 1 } } } //========================================================================= void CContinent::releaseSky() { // Remove the (old-style) sky if (!Sky.empty()) { SkyScene->deleteInstance (Sky); SkyScene->deleteInstance (Sky2ndPass); SkyScene->deleteInstance(SkyFogPart); Sky = NULL; Sky2ndPass = NULL; SkyFogPart = NULL; } deleteSkyScene(); // Release the (new-style) sky CurrentSky.release(); } //========================================================================= /*static*/ uint CContinent::getMaxNbUserLandMarks() { uint nbBonusLandmarks = (uint)IngameDbMngr.getProp( "INTERFACES:NB_BONUS_LANDMARKS" ); return STANDARD_NUM_USER_LANDMARKS + nbBonusLandmarks; }