From 6d557fec8e08dc65683454fde47d1d01a87faa9b Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 20 Sep 2017 11:42:15 -0700 Subject: [PATCH 01/17] Increase the far clip plane when distant terrain is enabled --- apps/openmw/mwrender/renderingmanager.cpp | 20 ++++++++++++-------- apps/openmw/mwrender/renderingmanager.hpp | 4 +++- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 3281a787b..b880943a4 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -179,8 +179,9 @@ namespace MWRender , mUnderwaterFog(0.f) , mUnderwaterIndoorFog(fallback->getFallbackFloat("Water_UnderwaterIndoorFog")) , mNightEyeFactor(0.f) - , mFieldOfViewOverride(0.f) + , mDistantTerrain(false) , mFieldOfViewOverridden(false) + , mFieldOfViewOverride(0.f) { resourceSystem->getSceneManager()->setParticleSystemMask(MWRender::Mask_ParticleSystem); resourceSystem->getSceneManager()->setShaderPath(resourcePath + "/shaders"); @@ -216,12 +217,12 @@ namespace MWRender mWater.reset(new Water(mRootNode, sceneRoot, mResourceSystem, mViewer->getIncrementalCompileOperation(), fallback, resourcePath)); - const bool distantTerrain = Settings::Manager::getBool("distant terrain", "Terrain"); + mDistantTerrain = Settings::Manager::getBool("distant terrain", "Terrain"); mTerrainStorage = new TerrainStorage(mResourceSystem, Settings::Manager::getString("normal map pattern", "Shaders"), Settings::Manager::getString("normal height map pattern", "Shaders"), - Settings::Manager::getBool("auto use terrain normal maps", "Shaders"), Settings::Manager::getString("terrain specular map pattern", "Shaders"), + Settings::Manager::getBool("auto use terrain normal maps", "Shaders"), Settings::Manager::getString("terrain specular map pattern", "Shaders"), Settings::Manager::getBool("auto use terrain specular maps", "Shaders")); - if (distantTerrain) + if (mDistantTerrain) mTerrain.reset(new Terrain::QuadTreeWorld(sceneRoot, mRootNode, mResourceSystem, mTerrainStorage, Mask_Terrain, Mask_PreCompile)); else mTerrain.reset(new Terrain::TerrainGrid(sceneRoot, mRootNode, mResourceSystem, mTerrainStorage, Mask_Terrain, Mask_PreCompile)); @@ -288,8 +289,10 @@ namespace MWRender mFirstPersonFieldOfView = Settings::Manager::getFloat("first person field of view", "Camera"); mStateUpdater->setFogEnd(mViewDistance); + mFarClip = mDistantTerrain ? 8192.0f*5.0f : mViewDistance; + mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform("near", mNearClip)); - mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform("far", mViewDistance)); + mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform("far", mFarClip)); mUniformNear = mRootNode->getOrCreateStateSet()->getUniform("near"); mUniformFar = mRootNode->getOrCreateStateSet()->getUniform("far"); @@ -632,7 +635,7 @@ namespace MWRender rttCamera->setRenderOrder(osg::Camera::PRE_RENDER); rttCamera->setReferenceFrame(osg::Camera::ABSOLUTE_RF); rttCamera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT, osg::Camera::PIXEL_BUFFER_RTT); - rttCamera->setProjectionMatrixAsPerspective(mFieldOfView, w/float(h), mNearClip, mViewDistance); + rttCamera->setProjectionMatrixAsPerspective(mFieldOfView, w/float(h), mNearClip, mFarClip); rttCamera->setViewMatrix(mViewer->getCamera()->getViewMatrix()); rttCamera->setViewport(0, 0, w, h); @@ -897,10 +900,10 @@ namespace MWRender float fov = mFieldOfView; if (mFieldOfViewOverridden) fov = mFieldOfViewOverride; - mViewer->getCamera()->setProjectionMatrixAsPerspective(fov, aspect, mNearClip, mViewDistance); + mViewer->getCamera()->setProjectionMatrixAsPerspective(fov, aspect, mNearClip, mFarClip); mUniformNear->set(mNearClip); - mUniformFar->set(mViewDistance); + mUniformFar->set(mFarClip); } void RenderingManager::updateTextureFiltering() @@ -960,6 +963,7 @@ namespace MWRender else if (it->first == "Camera" && it->second == "viewing distance") { mViewDistance = Settings::Manager::getFloat("viewing distance", "Camera"); + mFarClip = mDistantTerrain ? 8192.0f*5.0f : mViewDistance; mStateUpdater->setFogEnd(mViewDistance); updateProjectionMatrix(); } diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index f0087e43d..16854aaea 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -252,9 +252,11 @@ namespace MWRender float mNightEyeFactor; float mNearClip; + float mFarClip; float mViewDistance; + bool mDistantTerrain : 1; + bool mFieldOfViewOverridden : 1; float mFieldOfViewOverride; - bool mFieldOfViewOverridden; float mFieldOfView; float mFirstPersonFieldOfView; From 154cc8c6592cfe775f0953078ad1ceeb13afd398 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 21 Sep 2017 03:08:45 -0700 Subject: [PATCH 02/17] Use alternate fog parameters when distant land is enabled This allows the distant land to actually be seen when the user enables it. The values used are replicated from MGE XE's default settings and should probably be exposed somewhere. --- apps/openmw/mwrender/renderingmanager.cpp | 85 +++++++++++++++++------ apps/openmw/mwrender/renderingmanager.hpp | 8 ++- apps/openmw/mwrender/sky.hpp | 3 + apps/openmw/mwworld/weather.cpp | 35 ++++++---- apps/openmw/mwworld/weather.hpp | 9 +++ 5 files changed, 103 insertions(+), 37 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index b880943a4..ea0fef197 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -51,6 +51,20 @@ #include "terrainstorage.hpp" #include "util.hpp" +namespace +{ + // These values are what MGE XE uses by default when distant land is enabled, and are specified + // in cells (8192 units each). Should make them settings somewhere? Or wait to expose them + // "properly"? + const float DLRenderDistance = 5.0f; + const float DLLandFogStart = 2.0f; + const float DLLandFogEnd = 5.0f; + const float DLUnderwaterFogStart = -0.5f; + const float DLUnderwaterFogEnd = 0.3f; + const float DLInteriorFogStart = 0.0f; + const float DLInteriorFogEnd = 2.0f; +} + namespace MWRender { @@ -173,10 +187,12 @@ namespace MWRender , mResourceSystem(resourceSystem) , mWorkQueue(workQueue) , mUnrefQueue(new SceneUtil::UnrefQueue) - , mFogDepth(0.f) + , mLandFogStart(0.f) + , mLandFogEnd(std::numeric_limits::max()) + , mUnderwaterFogStart(0.f) + , mUnderwaterFogEnd(std::numeric_limits::max()) , mUnderwaterColor(fallback->getFallbackColour("Water_UnderwaterColor")) , mUnderwaterWeight(fallback->getFallbackFloat("Water_UnderwaterColorWeight")) - , mUnderwaterFog(0.f) , mUnderwaterIndoorFog(fallback->getFallbackFloat("Water_UnderwaterIndoorFog")) , mNightEyeFactor(0.f) , mDistantTerrain(false) @@ -289,7 +305,7 @@ namespace MWRender mFirstPersonFieldOfView = Settings::Manager::getFloat("first person field of view", "Camera"); mStateUpdater->setFogEnd(mViewDistance); - mFarClip = mDistantTerrain ? 8192.0f*5.0f : mViewDistance; + mFarClip = mDistantTerrain ? DLRenderDistance*8192.0f : mViewDistance; mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform("near", mNearClip)); mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform("far", mFarClip)); @@ -482,14 +498,44 @@ namespace MWRender { osg::Vec4f color = SceneUtil::colourFromRGB(cell->mAmbi.mFog); - configureFog (cell->mAmbi.mFogDensity, mUnderwaterIndoorFog, color); + if(mDistantTerrain) + { + float density = std::max(0.2f, cell->mAmbi.mFogDensity); + mLandFogStart = (DLInteriorFogEnd*(1.0f-density) + DLInteriorFogStart*density) * 8192.0f; + mLandFogEnd = DLInteriorFogEnd * 8192.0f; + mUnderwaterFogStart = DLUnderwaterFogStart * 8192.0f; + mUnderwaterFogEnd = DLUnderwaterFogEnd * 8192.0f; + mFogColor = color; + } + else + configureFog(cell->mAmbi.mFogDensity, mUnderwaterIndoorFog, 1.0f, 0.0f, color); } - void RenderingManager::configureFog(float fogDepth, float underwaterFog, const osg::Vec4f &color) + void RenderingManager::configureFog(float fogDepth, float underwaterFog, float dlFactor, float dlOffset, const osg::Vec4f &color) { - mFogDepth = fogDepth; + if(mDistantTerrain) + { + mLandFogStart = dlFactor * (DLLandFogStart - dlOffset*DLLandFogEnd) * 8192.0f; + mLandFogEnd = dlFactor * (1.0f-dlOffset) * DLLandFogEnd * 8192.0f; + mUnderwaterFogStart = DLUnderwaterFogStart * 8192.0f; + mUnderwaterFogEnd = DLUnderwaterFogEnd * 8192.0f; + } + else + { + if(fogDepth == 0.0) + { + mLandFogStart = 0.0f; + mLandFogEnd = std::numeric_limits::max(); + } + else + { + mLandFogStart = mViewDistance * (1 - fogDepth); + mLandFogEnd = mViewDistance; + } + mUnderwaterFogStart = mViewDistance * (1 - underwaterFog); + mUnderwaterFogEnd = mViewDistance; + } mFogColor = color; - mUnderwaterFog = underwaterFog; } SkyManager* RenderingManager::getSkyManager() @@ -520,23 +566,15 @@ namespace MWRender float viewDistance = mViewDistance; viewDistance = std::min(viewDistance, 6666.f); setFogColor(mUnderwaterColor * mUnderwaterWeight + mFogColor * (1.f-mUnderwaterWeight)); - mStateUpdater->setFogStart(viewDistance * (1 - mUnderwaterFog)); - mStateUpdater->setFogEnd(viewDistance); + mStateUpdater->setFogStart(mUnderwaterFogStart); + mStateUpdater->setFogEnd(mUnderwaterFogEnd); } else { setFogColor(mFogColor); - if (mFogDepth == 0.f) - { - mStateUpdater->setFogStart(0.f); - mStateUpdater->setFogEnd(std::numeric_limits::max()); - } - else - { - mStateUpdater->setFogStart(mViewDistance * (1 - mFogDepth)); - mStateUpdater->setFogEnd(mViewDistance); - } + mStateUpdater->setFogStart(mLandFogStart); + mStateUpdater->setFogEnd(mLandFogEnd); } } @@ -963,9 +1001,12 @@ namespace MWRender else if (it->first == "Camera" && it->second == "viewing distance") { mViewDistance = Settings::Manager::getFloat("viewing distance", "Camera"); - mFarClip = mDistantTerrain ? 8192.0f*5.0f : mViewDistance; - mStateUpdater->setFogEnd(mViewDistance); - updateProjectionMatrix(); + if(!mDistantTerrain) + { + mFarClip = mViewDistance; + mStateUpdater->setFogEnd(mViewDistance); + updateProjectionMatrix(); + } } else if (it->first == "General" && (it->second == "texture filter" || it->second == "texture mipmap" || diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 16854aaea..d44266e97 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -106,7 +106,7 @@ namespace MWRender void configureAmbient(const ESM::Cell* cell); void configureFog(const ESM::Cell* cell); - void configureFog(float fogDepth, float underwaterFog, const osg::Vec4f& colour); + void configureFog(float fogDepth, float underwaterFog, float dlFactor, float dlOffset, const osg::Vec4f& colour); void addCell(const MWWorld::CellStore* store); void removeCell(const MWWorld::CellStore* store); @@ -241,10 +241,12 @@ namespace MWRender osg::ref_ptr mStateUpdater; - float mFogDepth; + float mLandFogStart; + float mLandFogEnd; + float mUnderwaterFogStart; + float mUnderwaterFogEnd; osg::Vec4f mUnderwaterColor; float mUnderwaterWeight; - float mUnderwaterFog; float mUnderwaterIndoorFog; osg::Vec4f mFogColor; diff --git a/apps/openmw/mwrender/sky.hpp b/apps/openmw/mwrender/sky.hpp index 4357d468c..a9345cdb4 100644 --- a/apps/openmw/mwrender/sky.hpp +++ b/apps/openmw/mwrender/sky.hpp @@ -65,6 +65,9 @@ namespace MWRender float mFogDepth; + float mDLFogFactor; + float mDLFogOffset; + float mWindSpeed; float mCloudSpeed; diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index 2f0a2f8cf..bde4c4c22 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -103,6 +103,8 @@ Weather::Weather(const std::string& name, const Fallback::Map& fallback, float stormWindSpeed, float rainSpeed, + float dlFactor, + float dlOffset, const std::string& particleEffect) : mCloudTexture(fallback.getFallbackString("Weather_" + name + "_Cloud_Texture")) , mSkyColor(fallback.getFallbackColour("Weather_" + name +"_Sky_Sunrise_Color"), @@ -129,6 +131,7 @@ Weather::Weather(const std::string& name, , mWindSpeed(fallback.getFallbackFloat("Weather_" + name + "_Wind_Speed")) , mCloudSpeed(fallback.getFallbackFloat("Weather_" + name + "_Cloud_Speed")) , mGlareView(fallback.getFallbackFloat("Weather_" + name + "_Glare_View")) + , mDL{dlFactor, dlOffset} , mIsStorm(mWindSpeed > stormWindSpeed) , mRainSpeed(rainSpeed) , mRainFrequency(fallback.getFallbackFloat("Weather_" + name + "_Rain_Entrance_Speed")) @@ -541,16 +544,18 @@ WeatherManager::WeatherManager(MWRender::RenderingManager& rendering, const Fall mTimeSettings.mSunriseTime = mSunriseTime; mWeatherSettings.reserve(10); - addWeather("Clear", fallback); // 0 - addWeather("Cloudy", fallback); // 1 - addWeather("Foggy", fallback); // 2 - addWeather("Overcast", fallback); // 3 - addWeather("Rain", fallback); // 4 - addWeather("Thunderstorm", fallback); // 5 - addWeather("Ashstorm", fallback, "meshes\\ashcloud.nif"); // 6 - addWeather("Blight", fallback, "meshes\\blightcloud.nif"); // 7 - addWeather("Snow", fallback, "meshes\\snow.nif"); // 8 - addWeather("Blizzard", fallback, "meshes\\blizzard.nif"); // 9 + // These distant land fog factor and offset values are the defaults MGE XE provides. Should be + // provided by settings somewhere? + addWeather("Clear", fallback, 1.0f, 0.0f); // 0 + addWeather("Cloudy", fallback, 0.9f, 0.0f); // 1 + addWeather("Foggy", fallback, 0.2f, 30.0f); // 2 + addWeather("Overcast", fallback, 0.7f, 0.0f); // 3 + addWeather("Rain", fallback, 0.5f, 10.0f); // 4 + addWeather("Thunderstorm", fallback, 0.5f, 20.0f); // 5 + addWeather("Ashstorm", fallback, 0.2f, 50.0f, "meshes\\ashcloud.nif"); // 6 + addWeather("Blight", fallback, 0.2f, 60.0f, "meshes\\blightcloud.nif"); // 7 + addWeather("Snow", fallback, 0.5f, 40.0f, "meshes\\snow.nif"); // 8 + addWeather("Blizzard", fallback, 0.16f, 70.0f, "meshes\\blizzard.nif"); // 9 Store::iterator it = store.get().begin(); for(; it != store.get().end(); ++it) @@ -720,7 +725,8 @@ void WeatherManager::update(float duration, bool paused) mRendering.getSkyManager()->setMasserState(mMasser.calculateState(time)); mRendering.getSkyManager()->setSecundaState(mSecunda.calculateState(time)); - mRendering.configureFog(mResult.mFogDepth, underwaterFog, mResult.mFogColor); + mRendering.configureFog(mResult.mFogDepth, underwaterFog, mResult.mDLFogFactor, + mResult.mDLFogOffset/100.0f, mResult.mFogColor); mRendering.setAmbientColour(mResult.mAmbientColor); mRendering.setSunColour(mResult.mSunColor, mResult.mSunColor * mResult.mGlareView); @@ -866,11 +872,12 @@ void WeatherManager::clear() inline void WeatherManager::addWeather(const std::string& name, const Fallback::Map& fallback, + float dlFactor, float dlOffset, const std::string& particleEffect) { static const float fStromWindSpeed = mStore.get().find("fStromWindSpeed")->getFloat(); - Weather weather(name, fallback, fStromWindSpeed, mRainSpeed, particleEffect); + Weather weather(name, fallback, fStromWindSpeed, mRainSpeed, dlFactor, dlOffset, particleEffect); mWeatherSettings.push_back(weather); } @@ -1058,6 +1065,8 @@ inline void WeatherManager::calculateResult(const int weatherID, const float gam mResult.mNight = (gameHour < mSunriseTime || gameHour > mTimeSettings.mNightStart - 1); mResult.mFogDepth = current.mLandFogDepth.getValue(gameHour, mTimeSettings); + mResult.mDLFogFactor = current.mDL.FogFactor; + mResult.mDLFogOffset = current.mDL.FogOffset; mResult.mFogColor = current.mFogColor.getValue(gameHour, mTimeSettings); mResult.mAmbientColor = current.mAmbientColor.getValue(gameHour, mTimeSettings); mResult.mSunColor = current.mSunColor.getValue(gameHour, mTimeSettings); @@ -1113,6 +1122,8 @@ inline void WeatherManager::calculateTransitionResult(const float factor, const mResult.mAmbientColor = lerp(current.mAmbientColor, other.mAmbientColor, factor); mResult.mSunDiscColor = lerp(current.mSunDiscColor, other.mSunDiscColor, factor); mResult.mFogDepth = lerp(current.mFogDepth, other.mFogDepth, factor); + mResult.mDLFogFactor = lerp(current.mDLFogFactor, other.mDLFogFactor, factor); + mResult.mDLFogOffset = lerp(current.mDLFogOffset, other.mDLFogOffset, factor); mResult.mWindSpeed = lerp(current.mWindSpeed, other.mWindSpeed, factor); mResult.mCloudSpeed = lerp(current.mCloudSpeed, other.mCloudSpeed, factor); mResult.mGlareView = lerp(current.mGlareView, other.mGlareView, factor); diff --git a/apps/openmw/mwworld/weather.hpp b/apps/openmw/mwworld/weather.hpp index 84a6c5105..4555a9d2a 100644 --- a/apps/openmw/mwworld/weather.hpp +++ b/apps/openmw/mwworld/weather.hpp @@ -73,6 +73,8 @@ namespace MWWorld const Fallback::Map& fallback, float stormWindSpeed, float rainSpeed, + float dlFactor, + float dlOffset, const std::string& particleEffect); std::string mCloudTexture; @@ -102,6 +104,12 @@ namespace MWWorld // Also appears to modify how visible the sun, moons, and stars are for various weather effects. float mGlareView; + // Fog factor and offset used with distant land rendering. + struct { + float FogFactor; + float FogOffset; + } mDL; + // Sound effect // This is used for Blight, Ashstorm and Blizzard (Bloodmoon) std::string mAmbientLoopSoundID; @@ -293,6 +301,7 @@ namespace MWWorld void addWeather(const std::string& name, const Fallback::Map& fallback, + float dlFactor, float dlOffset, const std::string& particleEffect = ""); void importRegions(); From 4caa7c9674339e23d48ab076a425e272d7427c71 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 28 Sep 2017 16:35:36 -0700 Subject: [PATCH 03/17] Workaround older MSVC quirk --- apps/openmw/mwworld/weather.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index bde4c4c22..601379f08 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -131,7 +131,6 @@ Weather::Weather(const std::string& name, , mWindSpeed(fallback.getFallbackFloat("Weather_" + name + "_Wind_Speed")) , mCloudSpeed(fallback.getFallbackFloat("Weather_" + name + "_Cloud_Speed")) , mGlareView(fallback.getFallbackFloat("Weather_" + name + "_Glare_View")) - , mDL{dlFactor, dlOffset} , mIsStorm(mWindSpeed > stormWindSpeed) , mRainSpeed(rainSpeed) , mRainFrequency(fallback.getFallbackFloat("Weather_" + name + "_Rain_Entrance_Speed")) @@ -145,6 +144,8 @@ Weather::Weather(const std::string& name, , mFlashDecrement(fallback.getFallbackFloat("Weather_" + name + "_Flash_Decrement")) , mFlashBrightness(0.0f) { + mDL.FogFactor = dlFactor; + mDL.FogOffset = dlOffset; mThunderSoundID[0] = fallback.getFallbackString("Weather_" + name + "_Thunder_Sound_ID_0"); mThunderSoundID[1] = fallback.getFallbackString("Weather_" + name + "_Thunder_Sound_ID_1"); mThunderSoundID[2] = fallback.getFallbackString("Weather_" + name + "_Thunder_Sound_ID_2"); From 41669467aee26fb0f1e99d10b9120a0831bfcc2d Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 28 Sep 2017 17:34:44 -0700 Subject: [PATCH 04/17] Make settings for distant fog parameters --- apps/openmw/mwrender/renderingmanager.cpp | 54 +++++++++++++---------- apps/openmw/mwrender/renderingmanager.hpp | 1 + files/settings-default.cfg | 22 +++++++++ 3 files changed, 53 insertions(+), 24 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index ea0fef197..b3fee20b5 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -53,16 +53,13 @@ namespace { - // These values are what MGE XE uses by default when distant land is enabled, and are specified - // in cells (8192 units each). Should make them settings somewhere? Or wait to expose them - // "properly"? - const float DLRenderDistance = 5.0f; - const float DLLandFogStart = 2.0f; - const float DLLandFogEnd = 5.0f; - const float DLUnderwaterFogStart = -0.5f; - const float DLUnderwaterFogEnd = 0.3f; - const float DLInteriorFogStart = 0.0f; - const float DLInteriorFogEnd = 2.0f; + float DLRenderDistance; + float DLLandFogStart; + float DLLandFogEnd; + float DLUnderwaterFogStart; + float DLUnderwaterFogEnd; + float DLInteriorFogStart; + float DLInteriorFogEnd; } namespace MWRender @@ -195,6 +192,7 @@ namespace MWRender , mUnderwaterWeight(fallback->getFallbackFloat("Water_UnderwaterColorWeight")) , mUnderwaterIndoorFog(fallback->getFallbackFloat("Water_UnderwaterIndoorFog")) , mNightEyeFactor(0.f) + , mDistantFog(false) , mDistantTerrain(false) , mFieldOfViewOverridden(false) , mFieldOfViewOverride(0.f) @@ -233,6 +231,15 @@ namespace MWRender mWater.reset(new Water(mRootNode, sceneRoot, mResourceSystem, mViewer->getIncrementalCompileOperation(), fallback, resourcePath)); + DLRenderDistance = Settings::Manager::getFloat("distant viewing distance", "Camera"); + DLLandFogStart = Settings::Manager::getFloat("distant land fog start", "Fog"); + DLLandFogEnd = Settings::Manager::getFloat("distant land fog end", "Fog"); + DLUnderwaterFogStart = Settings::Manager::getFloat("distant underwater fog start", "Fog"); + DLUnderwaterFogEnd = Settings::Manager::getFloat("distant underwater fog end", "Fog"); + DLInteriorFogStart = Settings::Manager::getFloat("distant interior fog start", "Fog"); + DLInteriorFogEnd = Settings::Manager::getFloat("distant interior fog end", "Fog"); + + mDistantFog = Settings::Manager::getBool("use distant fog", "Fog"); mDistantTerrain = Settings::Manager::getBool("distant terrain", "Terrain"); mTerrainStorage = new TerrainStorage(mResourceSystem, Settings::Manager::getString("normal map pattern", "Shaders"), Settings::Manager::getString("normal height map pattern", "Shaders"), Settings::Manager::getBool("auto use terrain normal maps", "Shaders"), Settings::Manager::getString("terrain specular map pattern", "Shaders"), @@ -305,7 +312,7 @@ namespace MWRender mFirstPersonFieldOfView = Settings::Manager::getFloat("first person field of view", "Camera"); mStateUpdater->setFogEnd(mViewDistance); - mFarClip = mDistantTerrain ? DLRenderDistance*8192.0f : mViewDistance; + mFarClip = mDistantTerrain ? DLRenderDistance : mViewDistance; mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform("near", mNearClip)); mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform("far", mFarClip)); @@ -498,13 +505,13 @@ namespace MWRender { osg::Vec4f color = SceneUtil::colourFromRGB(cell->mAmbi.mFog); - if(mDistantTerrain) + if(mDistantFog) { float density = std::max(0.2f, cell->mAmbi.mFogDensity); - mLandFogStart = (DLInteriorFogEnd*(1.0f-density) + DLInteriorFogStart*density) * 8192.0f; - mLandFogEnd = DLInteriorFogEnd * 8192.0f; - mUnderwaterFogStart = DLUnderwaterFogStart * 8192.0f; - mUnderwaterFogEnd = DLUnderwaterFogEnd * 8192.0f; + mLandFogStart = (DLInteriorFogEnd*(1.0f-density) + DLInteriorFogStart*density); + mLandFogEnd = DLInteriorFogEnd; + mUnderwaterFogStart = DLUnderwaterFogStart; + mUnderwaterFogEnd = DLUnderwaterFogEnd; mFogColor = color; } else @@ -513,12 +520,12 @@ namespace MWRender void RenderingManager::configureFog(float fogDepth, float underwaterFog, float dlFactor, float dlOffset, const osg::Vec4f &color) { - if(mDistantTerrain) + if(mDistantFog) { - mLandFogStart = dlFactor * (DLLandFogStart - dlOffset*DLLandFogEnd) * 8192.0f; - mLandFogEnd = dlFactor * (1.0f-dlOffset) * DLLandFogEnd * 8192.0f; - mUnderwaterFogStart = DLUnderwaterFogStart * 8192.0f; - mUnderwaterFogEnd = DLUnderwaterFogEnd * 8192.0f; + mLandFogStart = dlFactor * (DLLandFogStart - dlOffset*DLLandFogEnd); + mLandFogEnd = dlFactor * (1.0f-dlOffset) * DLLandFogEnd; + mUnderwaterFogStart = DLUnderwaterFogStart; + mUnderwaterFogEnd = DLUnderwaterFogEnd; } else { @@ -1002,11 +1009,10 @@ namespace MWRender { mViewDistance = Settings::Manager::getFloat("viewing distance", "Camera"); if(!mDistantTerrain) - { mFarClip = mViewDistance; + if(!mDistantFog) mStateUpdater->setFogEnd(mViewDistance); - updateProjectionMatrix(); - } + updateProjectionMatrix(); } else if (it->first == "General" && (it->second == "texture filter" || it->second == "texture mipmap" || diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index d44266e97..da79fba98 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -256,6 +256,7 @@ namespace MWRender float mNearClip; float mFarClip; float mViewDistance; + bool mDistantFog : 1; bool mDistantTerrain : 1; bool mFieldOfViewOverridden : 1; float mFieldOfViewOverride; diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 63afa4438..c1d269e6f 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -25,6 +25,10 @@ small feature culling pixel size = 2.0 # can dramatically affect performance, see documentation for details. viewing distance = 6666.0 +# Maximum visible distance for distant terrain. Caution: setting this too high +# can increase the chance for Z-fighting (flickering artifacts). +distant viewing distance = 40960 + # Camera field of view in degrees (e.g. 30.0 to 110.0). # Does not affect the player's hands in the first person camera. field of view = 55.0 @@ -90,6 +94,24 @@ pointers cache size = 40 # If true, use paging and LOD algorithms to display the entire terrain. If false, only display terrain of the loaded cells distant terrain = false +[Fog] + +# If true, use extended fog parameters for distant terrain not controlled by +# viewing distance. If false, use the standard fog calculations. +use distant fog = false + +distant land fog start = 16384 + +distant land fog end = 40960 + +distant underwater fog start = -4096 + +distant underwater fog end = 2457.6 + +distant interior fog start = 0 + +distant interior fog end = 16384 + [Map] # Size of each exterior cell in pixels in the world map. (e.g. 12 to 24). From 01dbac7b1589e0c2458d5160c27bad1b212d1d31 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 4 Mar 2018 11:37:11 -0800 Subject: [PATCH 05/17] Don't use a different setting for distant viewing distance --- apps/openmw/mwrender/renderingmanager.cpp | 14 ++++---------- apps/openmw/mwrender/renderingmanager.hpp | 1 - files/settings-default.cfg | 4 ---- 3 files changed, 4 insertions(+), 15 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index b3fee20b5..6f0ddba3a 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -53,7 +53,6 @@ namespace { - float DLRenderDistance; float DLLandFogStart; float DLLandFogEnd; float DLUnderwaterFogStart; @@ -231,7 +230,6 @@ namespace MWRender mWater.reset(new Water(mRootNode, sceneRoot, mResourceSystem, mViewer->getIncrementalCompileOperation(), fallback, resourcePath)); - DLRenderDistance = Settings::Manager::getFloat("distant viewing distance", "Camera"); DLLandFogStart = Settings::Manager::getFloat("distant land fog start", "Fog"); DLLandFogEnd = Settings::Manager::getFloat("distant land fog end", "Fog"); DLUnderwaterFogStart = Settings::Manager::getFloat("distant underwater fog start", "Fog"); @@ -312,10 +310,8 @@ namespace MWRender mFirstPersonFieldOfView = Settings::Manager::getFloat("first person field of view", "Camera"); mStateUpdater->setFogEnd(mViewDistance); - mFarClip = mDistantTerrain ? DLRenderDistance : mViewDistance; - mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform("near", mNearClip)); - mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform("far", mFarClip)); + mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform("far", mViewDistance)); mUniformNear = mRootNode->getOrCreateStateSet()->getUniform("near"); mUniformFar = mRootNode->getOrCreateStateSet()->getUniform("far"); @@ -680,7 +676,7 @@ namespace MWRender rttCamera->setRenderOrder(osg::Camera::PRE_RENDER); rttCamera->setReferenceFrame(osg::Camera::ABSOLUTE_RF); rttCamera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT, osg::Camera::PIXEL_BUFFER_RTT); - rttCamera->setProjectionMatrixAsPerspective(mFieldOfView, w/float(h), mNearClip, mFarClip); + rttCamera->setProjectionMatrixAsPerspective(mFieldOfView, w/float(h), mNearClip, mViewDistance); rttCamera->setViewMatrix(mViewer->getCamera()->getViewMatrix()); rttCamera->setViewport(0, 0, w, h); @@ -945,10 +941,10 @@ namespace MWRender float fov = mFieldOfView; if (mFieldOfViewOverridden) fov = mFieldOfViewOverride; - mViewer->getCamera()->setProjectionMatrixAsPerspective(fov, aspect, mNearClip, mFarClip); + mViewer->getCamera()->setProjectionMatrixAsPerspective(fov, aspect, mNearClip, mViewDistance); mUniformNear->set(mNearClip); - mUniformFar->set(mFarClip); + mUniformFar->set(mViewDistance); } void RenderingManager::updateTextureFiltering() @@ -1008,8 +1004,6 @@ namespace MWRender else if (it->first == "Camera" && it->second == "viewing distance") { mViewDistance = Settings::Manager::getFloat("viewing distance", "Camera"); - if(!mDistantTerrain) - mFarClip = mViewDistance; if(!mDistantFog) mStateUpdater->setFogEnd(mViewDistance); updateProjectionMatrix(); diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index da79fba98..1c689d29f 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -254,7 +254,6 @@ namespace MWRender float mNightEyeFactor; float mNearClip; - float mFarClip; float mViewDistance; bool mDistantFog : 1; bool mDistantTerrain : 1; diff --git a/files/settings-default.cfg b/files/settings-default.cfg index c1d269e6f..dc770425f 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -25,10 +25,6 @@ small feature culling pixel size = 2.0 # can dramatically affect performance, see documentation for details. viewing distance = 6666.0 -# Maximum visible distance for distant terrain. Caution: setting this too high -# can increase the chance for Z-fighting (flickering artifacts). -distant viewing distance = 40960 - # Camera field of view in degrees (e.g. 30.0 to 110.0). # Does not affect the player's hands in the first person camera. field of view = 55.0 From 1aa3ed0c180812dff3608d34f6537ee432eb1346 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 31 Mar 2018 00:51:43 +0300 Subject: [PATCH 06/17] Font loader fallback character reorganization (fixes #1987) --- components/fontloader/fontloader.cpp | 114 ++++++++++++++++----------- 1 file changed, 69 insertions(+), 45 deletions(-) diff --git a/components/fontloader/fontloader.cpp b/components/fontloader/fontloader.cpp index aa92a01c6..d2c97953c 100644 --- a/components/fontloader/fontloader.cpp +++ b/components/fontloader/fontloader.cpp @@ -339,52 +339,76 @@ namespace Gui + MyGUI::utility::toString((fontSize-data[i].ascent))); code->addAttribute("size", MyGUI::IntSize(static_cast(data[i].width), static_cast(data[i].height))); - // More hacks! The french game uses several win1252 characters that are not included - // in the cp437 encoding of the font. Fall back to similar available characters. - if (mEncoding == ToUTF8::CP437) + // Fall back from unavailable Windows-1252 encoding symbols to similar characters available in the game fonts + std::multimap additional; // fallback glyph index, unicode + additional.insert(std::make_pair(156, 0x00A2)); // cent sign + additional.insert(std::make_pair(89, 0x00A5)); // yen sign + additional.insert(std::make_pair(221, 0x00A6)); // broken bar + additional.insert(std::make_pair(99, 0x00A9)); // copyright sign + additional.insert(std::make_pair(97, 0x00AA)); // prima ordinal indicator + additional.insert(std::make_pair(60, 0x00AB)); // double left-pointing angle quotation mark + additional.insert(std::make_pair(45, 0x00AD)); // soft hyphen + additional.insert(std::make_pair(114, 0x00AE)); // registered trademark symbol + additional.insert(std::make_pair(45, 0x00AF)); // macron + additional.insert(std::make_pair(241, 0x00B1)); // plus-minus sign + additional.insert(std::make_pair(50, 0x00B2)); // superscript two + additional.insert(std::make_pair(51, 0x00B3)); // superscript three + additional.insert(std::make_pair(44, 0x00B8)); // cedilla + additional.insert(std::make_pair(49, 0x00B9)); // superscript one + additional.insert(std::make_pair(111, 0x00BA)); // primo ordinal indicator + additional.insert(std::make_pair(62, 0x00BB)); // double right-pointing angle quotation mark + additional.insert(std::make_pair(63, 0x00BF)); // inverted question mark + additional.insert(std::make_pair(65, 0x00C6)); // latin capital ae ligature + additional.insert(std::make_pair(79, 0x00D8)); // latin capital o with stroke + additional.insert(std::make_pair(97, 0x00E6)); // latin small ae ligature + additional.insert(std::make_pair(111, 0x00F8)); // latin small o with stroke + additional.insert(std::make_pair(79, 0x0152)); // latin capital oe ligature + additional.insert(std::make_pair(111, 0x0153)); // latin small oe ligature + additional.insert(std::make_pair(83, 0x015A)); // latin capital s with caron + additional.insert(std::make_pair(115, 0x015B)); // latin small s with caron + additional.insert(std::make_pair(89, 0x0178)); // latin capital y with diaresis + additional.insert(std::make_pair(90, 0x017D)); // latin capital z with caron + additional.insert(std::make_pair(122, 0x017E)); // latin small z with caron + additional.insert(std::make_pair(102, 0x0192)); // latin small f with hook + additional.insert(std::make_pair(94, 0x02C6)); // circumflex modifier + additional.insert(std::make_pair(126, 0x02DC)); // small tilde + additional.insert(std::make_pair(69, 0x0401)); // cyrillic capital io (no diaeresis latin e is available) + additional.insert(std::make_pair(137, 0x0451)); // cyrillic small io + additional.insert(std::make_pair(45, 0x2012)); // figure dash + additional.insert(std::make_pair(45, 0x2013)); // en dash + additional.insert(std::make_pair(45, 0x2014)); // em dash + additional.insert(std::make_pair(39, 0x2018)); // left single quotation mark + additional.insert(std::make_pair(39, 0x2019)); // right single quotation mark + additional.insert(std::make_pair(44, 0x201A)); // single low quotation mark + additional.insert(std::make_pair(39, 0x201B)); // single high quotation mark (reversed) + additional.insert(std::make_pair(34, 0x201C)); // left double quotation mark + additional.insert(std::make_pair(34, 0x201D)); // right double quotation mark + additional.insert(std::make_pair(44, 0x201E)); // double low quotation mark + additional.insert(std::make_pair(34, 0x201F)); // double high quotation mark (reversed) + additional.insert(std::make_pair(43, 0x2020)); // dagger + additional.insert(std::make_pair(216, 0x2021)); // double dagger (note: this glyph is not available) + additional.insert(std::make_pair(46, 0x2026)); // ellipsis + additional.insert(std::make_pair(37, 0x2030)); // per mille sign + additional.insert(std::make_pair(60, 0x2039)); // single left-pointing angle quotation mark + additional.insert(std::make_pair(62, 0x203A)); // single right-pointing angle quotation mark + additional.insert(std::make_pair(101, 0x20AC)); // euro sign + additional.insert(std::make_pair(84, 0x2122)); // trademark sign + additional.insert(std::make_pair(45, 0x2212)); // minus sign + + for (std::multimap::iterator it = additional.begin(); it != additional.end(); ++it) { - std::multimap additional; // - additional.insert(std::make_pair(39, 0x2019)); // apostrophe - additional.insert(std::make_pair(45, 0x2013)); // dash - additional.insert(std::make_pair(45, 0x2014)); // dash - additional.insert(std::make_pair(34, 0x201D)); // right double quotation mark - additional.insert(std::make_pair(34, 0x201C)); // left double quotation mark - additional.insert(std::make_pair(44, 0x201A)); - additional.insert(std::make_pair(44, 0x201E)); - additional.insert(std::make_pair(43, 0x2020)); - additional.insert(std::make_pair(94, 0x02C6)); - additional.insert(std::make_pair(37, 0x2030)); - additional.insert(std::make_pair(83, 0x0160)); - additional.insert(std::make_pair(60, 0x2039)); - additional.insert(std::make_pair(79, 0x0152)); - additional.insert(std::make_pair(90, 0x017D)); - additional.insert(std::make_pair(39, 0x2019)); - additional.insert(std::make_pair(126, 0x02DC)); - additional.insert(std::make_pair(84, 0x2122)); - additional.insert(std::make_pair(83, 0x0161)); - additional.insert(std::make_pair(62, 0x203A)); - additional.insert(std::make_pair(111, 0x0153)); - additional.insert(std::make_pair(122, 0x017E)); - additional.insert(std::make_pair(89, 0x0178)); - additional.insert(std::make_pair(156, 0x00A2)); - additional.insert(std::make_pair(46, 0x2026)); - - for (std::multimap::iterator it = additional.begin(); it != additional.end(); ++it) - { - if (it->first != i) - continue; - - code = codes->createChild("Code"); - code->addAttribute("index", it->second); - code->addAttribute("coord", MyGUI::utility::toString(x1) + " " - + MyGUI::utility::toString(y1) + " " - + MyGUI::utility::toString(w) + " " - + MyGUI::utility::toString(h)); - code->addAttribute("advance", data[i].width); - code->addAttribute("bearing", MyGUI::utility::toString(data[i].kerning) + " " - + MyGUI::utility::toString((fontSize-data[i].ascent))); - code->addAttribute("size", MyGUI::IntSize(static_cast(data[i].width), static_cast(data[i].height))); - } + if (it->first != i) + continue; + code = codes->createChild("Code"); + code->addAttribute("index", it->second); + code->addAttribute("coord", MyGUI::utility::toString(x1) + " " + + MyGUI::utility::toString(y1) + " " + + MyGUI::utility::toString(w) + " " + + MyGUI::utility::toString(h)); + code->addAttribute("advance", data[i].width); + code->addAttribute("bearing", MyGUI::utility::toString(data[i].kerning) + " " + + MyGUI::utility::toString((fontSize-data[i].ascent))); + code->addAttribute("size", MyGUI::IntSize(static_cast(data[i].width), static_cast(data[i].height))); } // ASCII vertical bar, use this as text input cursor From 4f64ba7b7f7761ae4cf17a21fd5dda8a793c08a5 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 24 Mar 2018 13:43:18 +0300 Subject: [PATCH 07/17] Implement sBookSkillMessage GMST --- apps/openmw/mwmechanics/npcstats.cpp | 7 ++++++- apps/openmw/mwmechanics/npcstats.hpp | 2 +- apps/openmw/mwworld/actionread.cpp | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index f0fc7fb6e..f15152759 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -214,7 +214,7 @@ void MWMechanics::NpcStats::useSkill (int skillIndex, const ESM::Class& class_, } } -void MWMechanics::NpcStats::increaseSkill(int skillIndex, const ESM::Class &class_, bool preserveProgress) +void MWMechanics::NpcStats::increaseSkill(int skillIndex, const ESM::Class &class_, bool preserveProgress, bool readBook) { int base = getSkill (skillIndex).getBase(); @@ -256,9 +256,14 @@ void MWMechanics::NpcStats::increaseSkill(int skillIndex, const ESM::Class &clas MWBase::Environment::get().getWindowManager()->playSound("skillraise"); std::stringstream message; + + if (readBook) + message << std::string("#{sBookSkillMessage}\n"); + message << boost::format(MWBase::Environment::get().getWindowManager ()->getGameSettingString ("sNotifyMessage39", "")) % std::string("#{" + ESM::Skill::sSkillNameIds[skillIndex] + "}") % static_cast (base); + MWBase::Environment::get().getWindowManager ()->messageBox(message.str(), MWGui::ShowInDialogueMode_Never); if (mLevelProgress >= gmst.find("iLevelUpTotal")->getInt()) diff --git a/apps/openmw/mwmechanics/npcstats.hpp b/apps/openmw/mwmechanics/npcstats.hpp index 9ded47c07..d2e42e994 100644 --- a/apps/openmw/mwmechanics/npcstats.hpp +++ b/apps/openmw/mwmechanics/npcstats.hpp @@ -81,7 +81,7 @@ namespace MWMechanics void useSkill (int skillIndex, const ESM::Class& class_, int usageType = -1, float extraFactor=1.f); ///< Increase skill by usage. - void increaseSkill (int skillIndex, const ESM::Class& class_, bool preserveProgress); + void increaseSkill (int skillIndex, const ESM::Class& class_, bool preserveProgress, bool readBook = false); int getLevelProgress() const; diff --git a/apps/openmw/mwworld/actionread.cpp b/apps/openmw/mwworld/actionread.cpp index d2dc972fc..58611f0af 100644 --- a/apps/openmw/mwworld/actionread.cpp +++ b/apps/openmw/mwworld/actionread.cpp @@ -52,7 +52,7 @@ namespace MWWorld playerRef->mBase->mClass ); - npcStats.increaseSkill (ref->mBase->mData.mSkillId, *class_, true); + npcStats.increaseSkill (ref->mBase->mData.mSkillId, *class_, true, true); npcStats.flagAsUsed (ref->mBase->mId); } From 27c831959ec93dd581f8e2982922eb0965ad3607 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Mon, 2 Apr 2018 22:27:04 +0400 Subject: [PATCH 08/17] Handle OnPCHitMe variable (bug #2703) --- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 225427829..07f16cc50 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -1445,7 +1445,20 @@ namespace MWMechanics // Attacker is in combat with us, but we are not in combat with the attacker yet. Time to fight back. // Note: accidental or collateral damage attacks are ignored. if (!target.getClass().getCreatureStats(target).getAiSequence().hasPackage(AiPackage::TypeIdPursue)) - startCombat(target, attacker); + { + // If an actor has OnPCHitMe declared in his script, his Fight = 0 and the attacker is player, + // he will attack the player only if we will force him (e.g. via StartCombat console command) + bool peaceful = false; + std::string script = target.getClass().getScript(target); + if (!script.empty() && target.getRefData().getLocals().hasVar(script, "onpchitme") && attacker == getPlayer()) + { + int fight = std::max(0, target.getClass().getCreatureStats(target).getAiSetting(CreatureStats::AI_Fight).getModified()); + peaceful = (fight == 0); + } + + if (!peaceful) + startCombat(target, attacker); + } } return true; From c8abd11f5d0ede150a28b0dc64af65ccef0ea8e5 Mon Sep 17 00:00:00 2001 From: David Cernat Date: Thu, 5 Apr 2018 15:42:06 +0300 Subject: [PATCH 09/17] [General] Move creature disguises for players to PlayerShapeshift packet Additionally, make associated variables clearer, and move associated server script functions next to other shapeshifting functions. --- apps/openmw-mp/Script/Functions/Mechanics.cpp | 25 +++++++++++++ apps/openmw-mp/Script/Functions/Mechanics.hpp | 36 +++++++++++++++++++ apps/openmw-mp/Script/Functions/Stats.cpp | 26 -------------- apps/openmw-mp/Script/Functions/Stats.hpp | 6 ---- apps/openmw/mwmp/PlayerList.cpp | 18 +++++----- components/openmw-mp/Base/BasePlayer.hpp | 7 ++-- .../Packets/Player/PacketPlayerBaseInfo.cpp | 3 -- .../Packets/Player/PacketPlayerShapeshift.cpp | 3 ++ 8 files changed, 77 insertions(+), 47 deletions(-) diff --git a/apps/openmw-mp/Script/Functions/Mechanics.cpp b/apps/openmw-mp/Script/Functions/Mechanics.cpp index d4ddcef87..2635748bc 100644 --- a/apps/openmw-mp/Script/Functions/Mechanics.cpp +++ b/apps/openmw-mp/Script/Functions/Mechanics.cpp @@ -92,6 +92,22 @@ bool MechanicsFunctions::IsWerewolf(unsigned short pid) noexcept return player->isWerewolf; } +const char *MechanicsFunctions::GetCreatureRefId(unsigned short pid) noexcept +{ + Player *player; + GET_PLAYER(pid, player, 0); + + return player->creatureRefId.c_str(); +} + +bool MechanicsFunctions::DisplaysCreatureName(unsigned short pid) noexcept +{ + Player *player; + GET_PLAYER(pid, player, 0); + + return player->displayCreatureName; +} + void MechanicsFunctions::SetMarkCell(unsigned short pid, const char *cellDescription) noexcept { Player *player; @@ -143,6 +159,15 @@ void MechanicsFunctions::SetWerewolfState(unsigned short pid, bool isWerewolf) n player->isWerewolf = isWerewolf; } +void MechanicsFunctions::SetCreatureRefId(unsigned short pid, const char *refId, bool displayCreatureName) noexcept +{ + Player *player; + GET_PLAYER(pid, player, ); + + player->creatureRefId = refId; + player->displayCreatureName = displayCreatureName; +} + void MechanicsFunctions::SendMarkLocation(unsigned short pid) { Player *player; diff --git a/apps/openmw-mp/Script/Functions/Mechanics.hpp b/apps/openmw-mp/Script/Functions/Mechanics.hpp index f0e3e13d3..0ab87c303 100644 --- a/apps/openmw-mp/Script/Functions/Mechanics.hpp +++ b/apps/openmw-mp/Script/Functions/Mechanics.hpp @@ -16,6 +16,8 @@ \ {"GetScale", MechanicsFunctions::GetScale},\ {"IsWerewolf", MechanicsFunctions::IsWerewolf},\ + {"GetCreatureRefId", MechanicsFunctions::GetCreatureRefId},\ + {"DisplaysCreatureName", MechanicsFunctions::DisplaysCreatureName},\ \ {"SetMarkCell", MechanicsFunctions::SetMarkCell},\ {"SetMarkPos", MechanicsFunctions::SetMarkPos},\ @@ -24,6 +26,7 @@ \ {"SetScale", MechanicsFunctions::SetScale},\ {"SetWerewolfState", MechanicsFunctions::SetWerewolfState},\ + {"SetCreatureRefId", MechanicsFunctions::SetCreatureRefId},\ \ {"SendMarkLocation", MechanicsFunctions::SendMarkLocation},\ {"SendSelectedSpell", MechanicsFunctions::SendSelectedSpell},\ @@ -118,6 +121,25 @@ public: */ static bool IsWerewolf(unsigned short pid) noexcept; + /** + * \brief Get the refId of the creature the player is disguised as. + * + * \param pid The player ID. + * \return The creature refId. + */ + static const char *GetCreatureRefId(unsigned short pid) noexcept; + + /** + * \brief Check whether a player's name is replaced by that of the creature they are + * disguised as when other players hover over them. + * + * This is based on the last PlayerShapeshift packet received or sent for that player. + * + * \param pid The player ID. + * \return The creature name display state. + */ + static bool DisplaysCreatureName(unsigned short pid) noexcept; + /** * \brief Set the Mark cell of a player. * @@ -196,6 +218,20 @@ public: */ static void SetWerewolfState(unsigned short pid, bool isWerewolf) noexcept; + /** + * \brief Set the refId of the creature a player is disguised as. + * + * This changes the creature refId recorded for that player in the server memory, but + * does not by itself send a packet. + * + * \param pid The player ID. + * \param refId The creature refId. + * \param displaysCreatureName Whether the player's name appears as that of the creature + * when hovered over by others. + * \return void + */ + static void SetCreatureRefId(unsigned short pid, const char *refId, bool displayCreatureName) noexcept; + /** * \brief Send a PlayerMiscellaneous packet with a Mark location to a player. * diff --git a/apps/openmw-mp/Script/Functions/Stats.cpp b/apps/openmw-mp/Script/Functions/Stats.cpp index 141425246..32b22144a 100644 --- a/apps/openmw-mp/Script/Functions/Stats.cpp +++ b/apps/openmw-mp/Script/Functions/Stats.cpp @@ -118,22 +118,6 @@ const char *StatsFunctions::GetBirthsign(unsigned short pid) noexcept return player->birthsign.c_str(); } -const char *StatsFunctions::GetCreatureModel(unsigned short pid) noexcept -{ - Player *player; - GET_PLAYER(pid, player, 0); - - return player->creatureModel.c_str(); -} - -bool StatsFunctions::IsCreatureName(unsigned short pid) noexcept -{ - Player *player; - GET_PLAYER(pid, player, 0); - - return player->useCreatureName; -} - const char *StatsFunctions::GetDeathReason(unsigned short pid) noexcept { Player *player; @@ -346,16 +330,6 @@ void StatsFunctions::SetBirthsign(unsigned short pid, const char *sign) noexcept player->birthsign = sign; } -void StatsFunctions::SetCreatureModel(unsigned short pid, const char *name, bool useCreatureName) noexcept -{ - Player *player; - GET_PLAYER(pid, player, ); - - player->creatureModel = name; - player->useCreatureName = useCreatureName; - -} - void StatsFunctions::SetLevel(unsigned short pid, int value) noexcept { Player *player; diff --git a/apps/openmw-mp/Script/Functions/Stats.hpp b/apps/openmw-mp/Script/Functions/Stats.hpp index ff2421ef7..3d0da464d 100644 --- a/apps/openmw-mp/Script/Functions/Stats.hpp +++ b/apps/openmw-mp/Script/Functions/Stats.hpp @@ -19,8 +19,6 @@ {"GetHair", StatsFunctions::GetHairstyle},\ {"GetIsMale", StatsFunctions::GetIsMale},\ {"GetBirthsign", StatsFunctions::GetBirthsign},\ - {"GetCreatureModel", StatsFunctions::GetCreatureModel},\ - {"IsCreatureName", StatsFunctions::IsCreatureName},\ {"GetDeathReason", StatsFunctions::GetDeathReason},\ \ {"GetLevel", StatsFunctions::GetLevel},\ @@ -51,7 +49,6 @@ {"SetHair", StatsFunctions::SetHairstyle},\ {"SetIsMale", StatsFunctions::SetIsMale},\ {"SetBirthsign", StatsFunctions::SetBirthsign},\ - {"SetCreatureModel", StatsFunctions::SetCreatureModel},\ \ {"SetLevel", StatsFunctions::SetLevel},\ {"SetLevelProgress", StatsFunctions::SetLevelProgress},\ @@ -98,8 +95,6 @@ public: static const char *GetHairstyle(unsigned short pid) noexcept; static int GetIsMale(unsigned short pid) noexcept; static const char *GetBirthsign(unsigned short pid) noexcept; - static const char *GetCreatureModel(unsigned short pid) noexcept; - static bool IsCreatureName(unsigned short pid) noexcept; static const char *GetDeathReason(unsigned short pid) noexcept; static int GetLevel(unsigned short pid) noexcept; @@ -128,7 +123,6 @@ public: static void SetHairstyle(unsigned short pid, const char *style) noexcept; static void SetIsMale(unsigned short pid, int male) noexcept; static void SetBirthsign(unsigned short pid, const char *name) noexcept; - static void SetCreatureModel(unsigned short pid, const char *name, bool useCreatureName) noexcept; static void SetLevel(unsigned short pid, int value) noexcept; static void SetLevelProgress(unsigned short pid, int value) noexcept; diff --git a/apps/openmw/mwmp/PlayerList.cpp b/apps/openmw/mwmp/PlayerList.cpp index 8c636a2de..f154938fa 100644 --- a/apps/openmw/mwmp/PlayerList.cpp +++ b/apps/openmw/mwmp/PlayerList.cpp @@ -44,21 +44,21 @@ void PlayerList::createPlayer(RakNet::RakNetGUID guid) ESM::Creature creature; ESM::NPC npc; - if (!dedicPlayer->creatureModel.empty()) + if (!dedicPlayer->creatureRefId.empty()) { - const ESM::Creature *tmpCreature = world->getStore().get().search(dedicPlayer->creatureModel); + const ESM::Creature *tmpCreature = world->getStore().get().search(dedicPlayer->creatureRefId); if (tmpCreature == 0) { - dedicPlayer->creatureModel = ""; + dedicPlayer->creatureRefId = ""; createPlayer(guid); return; } creature = *tmpCreature; creature.mScript = ""; - if (!dedicPlayer->useCreatureName) + if (!dedicPlayer->displayCreatureName) creature.mName = dedicPlayer->npc.mName; LOG_APPEND(Log::LOG_INFO, "Player %s looks like %s", dedicPlayer->npc.mName.c_str(), - dedicPlayer->creatureModel.c_str()); + dedicPlayer->creatureRefId.c_str()); } else { @@ -82,8 +82,8 @@ void PlayerList::createPlayer(RakNet::RakNetGUID guid) if (dedicPlayer->reference) { bool isNPC = dedicPlayer->reference->getPtr().getTypeName() == typeid(ESM::NPC).name(); - if ((!dedicPlayer->creatureModel.empty() && isNPC) || - (dedicPlayer->creatureModel.empty() && !isNPC)) + if ((!dedicPlayer->creatureRefId.empty() && isNPC) || + (dedicPlayer->creatureRefId.empty() && !isNPC)) { if (dedicPlayer->reference) { @@ -106,7 +106,7 @@ void PlayerList::createPlayer(RakNet::RakNetGUID guid) if (dedicPlayer->state == 0) { string recid; - if (dedicPlayer->creatureModel.empty()) + if (dedicPlayer->creatureRefId.empty()) { LOG_APPEND(Log::LOG_INFO, "- Creating new NPC record"); npc.mId = "Dedicated Player"; @@ -158,7 +158,7 @@ void PlayerList::createPlayer(RakNet::RakNetGUID guid) MWWorld::Store *creature_store = const_cast *> (&store->get()); MWWorld::Store *npc_store = const_cast *> (&store->get()); - if (!dedicPlayer->creatureModel.empty()) + if (!dedicPlayer->creatureRefId.empty()) { if (!npc.mId.empty() || npc.mId != "Dedicated Player") { diff --git a/components/openmw-mp/Base/BasePlayer.hpp b/components/openmw-mp/Base/BasePlayer.hpp index 717ddd266..0544f3fb1 100644 --- a/components/openmw-mp/Base/BasePlayer.hpp +++ b/components/openmw-mp/Base/BasePlayer.hpp @@ -224,7 +224,7 @@ namespace mwmp inventoryChanges.count = 0; spellbookChanges.action = 0; spellbookChanges.count = 0; - useCreatureName = false; + displayCreatureName = false; } BasePlayer() @@ -287,8 +287,9 @@ namespace mwmp float scale; bool isWerewolf; - std::string creatureModel; - bool useCreatureName; + + bool displayCreatureName; + std::string creatureRefId; bool isChangingRegion; diff --git a/components/openmw-mp/Packets/Player/PacketPlayerBaseInfo.cpp b/components/openmw-mp/Packets/Player/PacketPlayerBaseInfo.cpp index e026e5798..051c26e83 100644 --- a/components/openmw-mp/Packets/Player/PacketPlayerBaseInfo.cpp +++ b/components/openmw-mp/Packets/Player/PacketPlayerBaseInfo.cpp @@ -25,7 +25,4 @@ void PacketPlayerBaseInfo::Packet(RakNet::BitStream *bs, bool send) RW(player->npc.mFlags, send); RW(player->birthsign, send, 1); - - RW(player->creatureModel, send, 1); - RW(player->useCreatureName, send); } diff --git a/components/openmw-mp/Packets/Player/PacketPlayerShapeshift.cpp b/components/openmw-mp/Packets/Player/PacketPlayerShapeshift.cpp index 352342ccd..a1757d20e 100644 --- a/components/openmw-mp/Packets/Player/PacketPlayerShapeshift.cpp +++ b/components/openmw-mp/Packets/Player/PacketPlayerShapeshift.cpp @@ -14,4 +14,7 @@ void PacketPlayerShapeshift::Packet(RakNet::BitStream *bs, bool send) RW(player->scale, send); RW(player->isWerewolf, send); + + RW(player->displayCreatureName, send); + RW(player->creatureRefId, send, 1); } From 77467f17c9259d8dc23d0b415f578e4cd2bf7db1 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Fri, 6 Apr 2018 00:10:48 +0300 Subject: [PATCH 10/17] Fix a typo --- apps/openmw/mwmechanics/spellcasting.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index f43fd906d..0f56ea7f1 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -568,7 +568,7 @@ namespace MWMechanics ActiveSpells::ActiveEffect effect_ = effect; effect_.mMagnitude *= -1; absorbEffects.push_back(effect_); - if (reflected && Settings::Manager::getBool("classic reflected absorb attribute behavior", "Game")) + if (reflected && Settings::Manager::getBool("classic reflect absorb attribute behavior", "Game")) target.getClass().getCreatureStats(target).getActiveSpells().addSpell("", true, absorbEffects, mSourceName, caster.getClass().getCreatureStats(caster).getActorId()); else From 14f90e773d6864e7217cd9139c2809934dfd87b7 Mon Sep 17 00:00:00 2001 From: David Cernat Date: Sun, 8 Apr 2018 10:56:33 +0300 Subject: [PATCH 11/17] [Client] Split up creation of DedicatedPlayers into multiple methods Additionally, print player guids using their string representations for consistency. The creation and updating of DedicatedPlayer references remains very inelegant, but this commit is the first step towards fixing that. --- apps/openmw/mwmp/DedicatedPlayer.cpp | 215 +++++++++++++++++- apps/openmw/mwmp/DedicatedPlayer.hpp | 12 +- apps/openmw/mwmp/GUIController.cpp | 2 +- apps/openmw/mwmp/PlayerList.cpp | 166 +------------- apps/openmw/mwmp/PlayerList.hpp | 1 - .../player/ProcessorPlayerBaseInfo.hpp | 2 +- 6 files changed, 221 insertions(+), 177 deletions(-) diff --git a/apps/openmw/mwmp/DedicatedPlayer.cpp b/apps/openmw/mwmp/DedicatedPlayer.cpp index 1333f5b1d..a30d60939 100644 --- a/apps/openmw/mwmp/DedicatedPlayer.cpp +++ b/apps/openmw/mwmp/DedicatedPlayer.cpp @@ -1,7 +1,3 @@ -// -// Created by koncord on 02.01.16. -// - #include #include #include @@ -130,6 +126,56 @@ void DedicatedPlayer::move(float dt) MWMechanics::zTurn(ptr, position.rot[2], osg::DegreesToRadians(1.0)); } +void DedicatedPlayer::setBaseInfo() +{ + MWBase::World *world = MWBase::Environment::get().getWorld(); + + ESM::Creature tempCreature; + ESM::NPC tempNpc; + + if (!creatureRefId.empty()) + { + const ESM::Creature *tmpCreature = world->getStore().get().search(creatureRefId); + if (tmpCreature == 0) + { + creatureRefId = ""; + } + else + { + tempCreature = getCreatureRecord(); + } + } + + if (creatureRefId.empty()) + { + tempNpc = getNpcRecord(); + } + + bool reset = false; + if (reference) + { + deleteReference(); + reset = true; + } + + if (state == 0) + { + createReference(tempNpc, tempCreature, reset); + } + else + { + updateReference(tempNpc, tempCreature); + } + + state = 2; + + // Give this new character a fatigue of at least 1 so it doesn't spawn + // on the ground + creatureStats.mDynamic[2].mBase = 1; + + world->enable(ptr); +} + void DedicatedPlayer::setAnimFlags() { using namespace MWMechanics; @@ -302,14 +348,167 @@ void DedicatedPlayer::playSpeech() winMgr->messageBox(MWBase::Environment::get().getDialogueManager()->getVoiceCaption(sound), MWGui::ShowInDialogueMode_Never); } -MWWorld::Ptr DedicatedPlayer::getPtr() + +ESM::Creature DedicatedPlayer::getCreatureRecord() { - return ptr; + MWBase::World *world = MWBase::Environment::get().getWorld(); + + ESM::Creature creature; + + const ESM::Creature *tmpCreature = world->getStore().get().search(creatureRefId); + + creature = *tmpCreature; + creature.mScript = ""; + if (!displayCreatureName) + creature.mName = npc.mName; + LOG_APPEND(Log::LOG_INFO, "Player %s looks like %s", npc.mName.c_str(), creatureRefId.c_str()); + + return creature; +} + +ESM::NPC DedicatedPlayer::getNpcRecord() +{ + MWBase::World *world = MWBase::Environment::get().getWorld(); + + MWWorld::Ptr player = world->getPlayerPtr(); + + ESM::NPC newNpc = *player.get()->mBase; + + // To avoid freezes caused by invalid races, only set race if we find it + // on our client + if (world->getStore().get().search(npc.mRace) != 0) + newNpc.mRace = npc.mRace; + + newNpc.mHead = npc.mHead; + newNpc.mHair = npc.mHair; + newNpc.mClass = npc.mClass; + newNpc.mName = npc.mName; + newNpc.mFlags = npc.mFlags; + + return newNpc; +} + +void DedicatedPlayer::createReference(ESM::NPC& npc, ESM::Creature& creature, bool reset) +{ + MWBase::World *world = MWBase::Environment::get().getWorld(); + + // Temporarily spawn or move player to the center of exterior 0, 0 + ESM::Position spawnPos; + spawnPos.pos[0] = spawnPos.pos[1] = Main::get().getCellController()->getCellSize() / 2; + spawnPos.pos[2] = 0; + MWWorld::CellStore *cellStore = world->getExterior(0, 0); + + string recid; + if (creatureRefId.empty()) + { + LOG_APPEND(Log::LOG_INFO, "- Creating new NPC record"); + npc.mId = "Dedicated Player"; + recid = world->createRecord(npc)->mId; + } + else + { + LOG_APPEND(Log::LOG_INFO, "- Creating new Creature record"); + creature.mId = "Dedicated Player"; + recid = world->createRecord(creature)->mId; + } + + reference = new MWWorld::ManualRef(world->getStore(), recid, 1); + + LOG_APPEND(Log::LOG_INFO, "- Creating new reference pointer for %s", this->npc.mName.c_str()); + + MWWorld::Ptr tmp; + + if (reset) + { + if (cell.isExterior()) + cellStore = world->getExterior(cell.mData.mX, cell.mData.mY); + else + cellStore = world->getInterior(cell.mName); + + spawnPos = position; + } + + tmp = world->placeObject(reference->getPtr(), cellStore, spawnPos); + + ptr.mCell = tmp.mCell; + ptr.mRef = tmp.mRef; + + if (!reset) + { + cell = *ptr.getCell()->getCell(); + position = ptr.getRefData().getPosition(); + } + + ESM::CustomMarker mEditingMarker = Main::get().getGUIController()->createMarker(guid); + marker = mEditingMarker; + setMarkerState(true); } -MWWorld::Ptr DedicatedPlayer::getLiveCellPtr() +void DedicatedPlayer::updateReference(ESM::NPC& npc, ESM::Creature& creature) { - return reference->getPtr(); + MWBase::World *world = MWBase::Environment::get().getWorld(); + + // Temporarily spawn or move player to the center of exterior 0, 0 + ESM::Position spawnPos; + spawnPos.pos[0] = spawnPos.pos[1] = Main::get().getCellController()->getCellSize() / 2; + spawnPos.pos[2] = 0; + MWWorld::CellStore *cellStore = world->getExterior(0, 0); + + LOG_APPEND(Log::LOG_INFO, "- Updating reference pointer for %s", npc.mName.c_str()); + + MWWorld::ESMStore *store = const_cast(&world->getStore()); + MWWorld::Store *creature_store = const_cast *> (&store->get()); + MWWorld::Store *npc_store = const_cast *> (&store->get()); + + if (!creatureRefId.empty()) + { + if (!npc.mId.empty() || npc.mId != "Dedicated Player") + { + LOG_APPEND(Log::LOG_INFO, "- Deleting NPC record"); + npc_store->erase(npc.mId); + npc.mId.clear(); + } + creature.mId = ptr.get()->mBase->mId; + creature_store->insert(creature); + } + else + { + if (!creature.mId.empty() || creature.mId != "Dedicated Player") + { + LOG_APPEND(Log::LOG_INFO, "- Deleting Creature record"); + creature_store->erase(creature.mId); + creature.mId.clear(); + } + npc.mId = ptr.get()->mBase->mId; + npc_store->insert(npc); + } + + // Disable Ptr to avoid graphical glitches caused by race changes + world->disable(ptr); + + setPtr(world->moveObject(ptr, cellStore, spawnPos.pos[0], spawnPos.pos[1], spawnPos.pos[2])); + setCell(); +} + +void DedicatedPlayer::deleteReference() +{ + MWBase::World *world = MWBase::Environment::get().getWorld(); + + bool isNPC = reference->getPtr().getTypeName() == typeid(ESM::NPC).name(); + if ((!creatureRefId.empty() && isNPC) || + (creatureRefId.empty() && !isNPC)) + { + LOG_APPEND(Log::LOG_INFO, "- Deleting old reference"); + state = 0; + world->deleteObject(ptr); + delete reference; + reference = nullptr; + } +} + +MWWorld::Ptr DedicatedPlayer::getPtr() +{ + return ptr; } MWWorld::ManualRef *DedicatedPlayer::getRef() diff --git a/apps/openmw/mwmp/DedicatedPlayer.hpp b/apps/openmw/mwmp/DedicatedPlayer.hpp index 781748fb8..b141fa463 100644 --- a/apps/openmw/mwmp/DedicatedPlayer.hpp +++ b/apps/openmw/mwmp/DedicatedPlayer.hpp @@ -6,9 +6,12 @@ #define OPENMW_DEDICATEDPLAYER_HPP #include +#include #include #include +#include "../mwclass/npc.hpp" + #include "../mwmechanics/aisequence.hpp" #include "../mwworld/manualref.hpp" @@ -34,6 +37,7 @@ namespace mwmp void update(float dt); void move(float dt); + void setBaseInfo(); void setAnimFlags(); void setEquipment(); void setCell(); @@ -46,8 +50,14 @@ namespace mwmp void playAnimation(); void playSpeech(); + ESM::NPC getNpcRecord(); + ESM::Creature getCreatureRecord(); + + void createReference(ESM::NPC& npc, ESM::Creature& creature, bool reset); + void updateReference(ESM::NPC& npc, ESM::Creature& creature); + void deleteReference(); + MWWorld::Ptr getPtr(); - MWWorld::Ptr getLiveCellPtr(); MWWorld::ManualRef* getRef(); void setPtr(const MWWorld::Ptr& newPtr); diff --git a/apps/openmw/mwmp/GUIController.cpp b/apps/openmw/mwmp/GUIController.cpp index 2d5fb1a97..50525aa27 100644 --- a/apps/openmw/mwmp/GUIController.cpp +++ b/apps/openmw/mwmp/GUIController.cpp @@ -285,7 +285,7 @@ ESM::CustomMarker mwmp::GUIController::createMarker(const RakNet::RakNetGUID &gu ESM::CustomMarker mEditingMarker; if (!player) { - LOG_MESSAGE_SIMPLE(Log::LOG_ERROR, "Unknown player guid: %lu", guid.g); + LOG_MESSAGE_SIMPLE(Log::LOG_ERROR, "Unknown player guid: %s", guid.ToString()); return mEditingMarker; } diff --git a/apps/openmw/mwmp/PlayerList.cpp b/apps/openmw/mwmp/PlayerList.cpp index f154938fa..c7d064d9b 100644 --- a/apps/openmw/mwmp/PlayerList.cpp +++ b/apps/openmw/mwmp/PlayerList.cpp @@ -34,173 +34,9 @@ void PlayerList::update(float dt) } } -void PlayerList::createPlayer(RakNet::RakNetGUID guid) -{ - LOG_APPEND(Log::LOG_INFO, "- Setting up character info"); - - MWBase::World *world = MWBase::Environment::get().getWorld(); - - DedicatedPlayer *dedicPlayer = players[guid]; - - ESM::Creature creature; - ESM::NPC npc; - if (!dedicPlayer->creatureRefId.empty()) - { - const ESM::Creature *tmpCreature = world->getStore().get().search(dedicPlayer->creatureRefId); - if (tmpCreature == 0) - { - dedicPlayer->creatureRefId = ""; - createPlayer(guid); - return; - } - creature = *tmpCreature; - creature.mScript = ""; - if (!dedicPlayer->displayCreatureName) - creature.mName = dedicPlayer->npc.mName; - LOG_APPEND(Log::LOG_INFO, "Player %s looks like %s", dedicPlayer->npc.mName.c_str(), - dedicPlayer->creatureRefId.c_str()); - } - else - { - MWWorld::Ptr player = world->getPlayerPtr(); - - npc = *player.get()->mBase; - - // To avoid freezes caused by invalid races, only set race if we find it - // on our client - if (world->getStore().get().search(dedicPlayer->npc.mRace) != 0) - npc.mRace = dedicPlayer->npc.mRace; - - npc.mHead = dedicPlayer->npc.mHead; - npc.mHair = dedicPlayer->npc.mHair; - npc.mClass = dedicPlayer->npc.mClass; - npc.mName = dedicPlayer->npc.mName; - npc.mFlags = dedicPlayer->npc.mFlags; - } - - bool reset = false; - if (dedicPlayer->reference) - { - bool isNPC = dedicPlayer->reference->getPtr().getTypeName() == typeid(ESM::NPC).name(); - if ((!dedicPlayer->creatureRefId.empty() && isNPC) || - (dedicPlayer->creatureRefId.empty() && !isNPC)) - { - if (dedicPlayer->reference) - { - LOG_APPEND(Log::LOG_INFO, "- Deleting old reference"); - dedicPlayer->state = 0; - world->deleteObject(dedicPlayer->ptr); - delete dedicPlayer->reference; - dedicPlayer->reference = nullptr; - reset = true; - } - } - } - - // Temporarily spawn or move player to the center of exterior 0,0 whenever setting base info - ESM::Position spawnPos; - spawnPos.pos[0] = spawnPos.pos[1] = Main::get().getCellController()->getCellSize() / 2; - spawnPos.pos[2] = 0; - MWWorld::CellStore *cellStore = world->getExterior(0, 0); - - if (dedicPlayer->state == 0) - { - string recid; - if (dedicPlayer->creatureRefId.empty()) - { - LOG_APPEND(Log::LOG_INFO, "- Creating new NPC record"); - npc.mId = "Dedicated Player"; - recid = world->createRecord(npc)->mId; - } - else - { - LOG_APPEND(Log::LOG_INFO, "- Creating new Creature record"); - creature.mId = "Dedicated Player"; - recid = world->createRecord(creature)->mId; - } - - dedicPlayer->reference = new MWWorld::ManualRef(world->getStore(), recid, 1); - - LOG_APPEND(Log::LOG_INFO, "- Creating new reference pointer for %s", dedicPlayer->npc.mName.c_str()); - - MWWorld::Ptr tmp; - - if (reset) - { - if (dedicPlayer->cell.isExterior()) - cellStore = world->getExterior(dedicPlayer->cell.mData.mX, dedicPlayer->cell.mData.mY); - else - cellStore = world->getInterior(dedicPlayer->cell.mName); - - spawnPos = dedicPlayer->position; - } - - tmp = world->placeObject(dedicPlayer->reference->getPtr(), cellStore, spawnPos); - - dedicPlayer->ptr.mCell = tmp.mCell; - dedicPlayer->ptr.mRef = tmp.mRef; - - if (!reset) - { - dedicPlayer->cell = *dedicPlayer->ptr.getCell()->getCell(); - dedicPlayer->position = dedicPlayer->ptr.getRefData().getPosition(); - } - - ESM::CustomMarker mEditingMarker = Main::get().getGUIController()->createMarker(guid); - dedicPlayer->marker = mEditingMarker; - dedicPlayer->setMarkerState(true); - } - else - { - LOG_APPEND(Log::LOG_INFO, "- Updating reference pointer for %s", dedicPlayer->npc.mName.c_str()); - - MWWorld::ESMStore *store = const_cast(&world->getStore()); - MWWorld::Store *creature_store = const_cast *> (&store->get()); - MWWorld::Store *npc_store = const_cast *> (&store->get()); - - if (!dedicPlayer->creatureRefId.empty()) - { - if (!npc.mId.empty() || npc.mId != "Dedicated Player") - { - LOG_APPEND(Log::LOG_INFO, "- Deleting NPC record"); - npc_store->erase(npc.mId); - npc.mId.clear(); - } - creature.mId = players[guid]->ptr.get()->mBase->mId; - creature_store->insert(creature); - } - else - { - if (!creature.mId.empty() || creature.mId != "Dedicated Player") - { - LOG_APPEND(Log::LOG_INFO, "- Deleting Creature record"); - creature_store->erase(creature.mId); - creature.mId.clear(); - } - npc.mId = players[guid]->ptr.get()->mBase->mId; - npc_store->insert(npc); - } - - // Disable Ptr to avoid graphical glitches caused by race changes - world->disable(players[guid]->ptr); - - dedicPlayer->setPtr(world->moveObject(dedicPlayer->ptr, cellStore, spawnPos.pos[0], spawnPos.pos[1], spawnPos.pos[2])); - dedicPlayer->setCell(); - } - - dedicPlayer->guid = guid; - dedicPlayer->state = 2; - - // Give this new character a fatigue of at least 1 so it doesn't spawn - // on the ground - dedicPlayer->creatureStats.mDynamic[2].mBase = 1; - - world->enable(players[guid]->ptr); -} - DedicatedPlayer *PlayerList::newPlayer(RakNet::RakNetGUID guid) { - LOG_APPEND(Log::LOG_INFO, "- Creating new DedicatedPlayer with guid %lu", guid.g); + LOG_APPEND(Log::LOG_INFO, "- Creating new DedicatedPlayer with guid %s", guid.ToString()); players[guid] = new DedicatedPlayer(guid); players[guid]->state = 0; diff --git a/apps/openmw/mwmp/PlayerList.hpp b/apps/openmw/mwmp/PlayerList.hpp index 84c39077e..91f03d869 100644 --- a/apps/openmw/mwmp/PlayerList.hpp +++ b/apps/openmw/mwmp/PlayerList.hpp @@ -27,7 +27,6 @@ namespace mwmp static void update(float dt); - static void createPlayer(RakNet::RakNetGUID guid); static DedicatedPlayer *newPlayer(RakNet::RakNetGUID guid); static void disconnectPlayer(RakNet::RakNetGUID guid); diff --git a/apps/openmw/mwmp/processors/player/ProcessorPlayerBaseInfo.hpp b/apps/openmw/mwmp/processors/player/ProcessorPlayerBaseInfo.hpp index a8e805f5b..026a1ca1e 100644 --- a/apps/openmw/mwmp/processors/player/ProcessorPlayerBaseInfo.hpp +++ b/apps/openmw/mwmp/processors/player/ProcessorPlayerBaseInfo.hpp @@ -49,7 +49,7 @@ namespace mwmp packet.Read(); } - PlayerList::createPlayer(guid); + static_cast(player)->setBaseInfo(); } } }; From 141eb8b7c249f87b8369a71a5f8ee41edb1c5532 Mon Sep 17 00:00:00 2001 From: David Cernat Date: Sun, 8 Apr 2018 15:02:43 +0300 Subject: [PATCH 12/17] [Client] Streamline creation of references for DedicatedPlayers Additionally, delete DedicatedPlayers who disconnect. Previously, all disconnected DedicatedPlayers were still kept in memory, but never used again. There was code that suggested they were meant to be reused upon reconnecting, but that reuse had never actually been implemented, and would probably not be that useful anyway. --- apps/openmw/mwmp/DedicatedPlayer.cpp | 141 ++++-------------- apps/openmw/mwmp/DedicatedPlayer.hpp | 6 +- apps/openmw/mwmp/PlayerList.cpp | 27 +--- apps/openmw/mwmp/PlayerList.hpp | 2 +- .../player/ProcessorUserDisconnected.hpp | 2 +- 5 files changed, 39 insertions(+), 139 deletions(-) diff --git a/apps/openmw/mwmp/DedicatedPlayer.cpp b/apps/openmw/mwmp/DedicatedPlayer.cpp index a30d60939..c218f3846 100644 --- a/apps/openmw/mwmp/DedicatedPlayer.cpp +++ b/apps/openmw/mwmp/DedicatedPlayer.cpp @@ -46,6 +46,10 @@ DedicatedPlayer::DedicatedPlayer(RakNet::RakNetGUID guid) : BasePlayer(guid) creatureStats.mDead = false; movementFlags = 0; attack.instant = false; + + cell.blank(); + position.pos[0] = position.pos[1] = Main::get().getCellController()->getCellSize() / 2; + position.pos[2] = 0; } DedicatedPlayer::~DedicatedPlayer() { @@ -94,7 +98,7 @@ void DedicatedPlayer::update(float dt) void DedicatedPlayer::move(float dt) { - if (state != 2) return; + if (!reference) return; ESM::Position refPos = ptr.getRefData().getPosition(); MWBase::World *world = MWBase::Environment::get().getWorld(); @@ -151,31 +155,26 @@ void DedicatedPlayer::setBaseInfo() tempNpc = getNpcRecord(); } - bool reset = false; if (reference) { deleteReference(); - reset = true; - } - - if (state == 0) - { - createReference(tempNpc, tempCreature, reset); - } - else - { - updateReference(tempNpc, tempCreature); } - state = 2; + createReference(tempNpc, tempCreature); - // Give this new character a fatigue of at least 1 so it doesn't spawn - // on the ground - creatureStats.mDynamic[2].mBase = 1; + // Give this new character a temporary high fatigue of at least 1 so it doesn't + // spawn on the ground + creatureStats.mDynamic[2].mBase = 1000; world->enable(ptr); } +void DedicatedPlayer::setShapeshift() +{ + MWBase::Environment::get().getWorld()->scaleObject(ptr, scale); + MWBase::Environment::get().getMechanicsManager()->setWerewolf(ptr, isWerewolf); +} + void DedicatedPlayer::setAnimFlags() { using namespace MWMechanics; @@ -250,9 +249,8 @@ void DedicatedPlayer::setEquipment() void DedicatedPlayer::setCell() { - // Prevent cell update when player hasn't been instantiated yet - if (state == 0) - return; + // Prevent cell update when reference doesn't exist + if (!reference) return; MWBase::World *world = MWBase::Environment::get().getWorld(); @@ -287,12 +285,6 @@ void DedicatedPlayer::setCell() Main::get().getCellController()->getCell(cell)->updateLocal(true); } -void DedicatedPlayer::setShapeshift() -{ - MWBase::Environment::get().getWorld()->scaleObject(ptr, scale); - MWBase::Environment::get().getMechanicsManager()->setWerewolf(ptr, isWerewolf); -} - void DedicatedPlayer::updateMarker() { if (!markerEnabled) @@ -388,122 +380,43 @@ ESM::NPC DedicatedPlayer::getNpcRecord() return newNpc; } -void DedicatedPlayer::createReference(ESM::NPC& npc, ESM::Creature& creature, bool reset) +void DedicatedPlayer::createReference(ESM::NPC& npc, ESM::Creature& creature) { MWBase::World *world = MWBase::Environment::get().getWorld(); - // Temporarily spawn or move player to the center of exterior 0, 0 - ESM::Position spawnPos; - spawnPos.pos[0] = spawnPos.pos[1] = Main::get().getCellController()->getCellSize() / 2; - spawnPos.pos[2] = 0; - MWWorld::CellStore *cellStore = world->getExterior(0, 0); - - string recid; + string recId; if (creatureRefId.empty()) { LOG_APPEND(Log::LOG_INFO, "- Creating new NPC record"); npc.mId = "Dedicated Player"; - recid = world->createRecord(npc)->mId; + recId = world->createRecord(npc)->mId; } else { LOG_APPEND(Log::LOG_INFO, "- Creating new Creature record"); creature.mId = "Dedicated Player"; - recid = world->createRecord(creature)->mId; + recId = world->createRecord(creature)->mId; } - reference = new MWWorld::ManualRef(world->getStore(), recid, 1); + reference = new MWWorld::ManualRef(world->getStore(), recId, 1); LOG_APPEND(Log::LOG_INFO, "- Creating new reference pointer for %s", this->npc.mName.c_str()); - MWWorld::Ptr tmp; - - if (reset) - { - if (cell.isExterior()) - cellStore = world->getExterior(cell.mData.mX, cell.mData.mY); - else - cellStore = world->getInterior(cell.mName); - - spawnPos = position; - } - - tmp = world->placeObject(reference->getPtr(), cellStore, spawnPos); - - ptr.mCell = tmp.mCell; - ptr.mRef = tmp.mRef; - - if (!reset) - { - cell = *ptr.getCell()->getCell(); - position = ptr.getRefData().getPosition(); - } + ptr = world->placeObject(reference->getPtr(), Main::get().getCellController()->getCellStore(cell), position); ESM::CustomMarker mEditingMarker = Main::get().getGUIController()->createMarker(guid); marker = mEditingMarker; setMarkerState(true); } -void DedicatedPlayer::updateReference(ESM::NPC& npc, ESM::Creature& creature) -{ - MWBase::World *world = MWBase::Environment::get().getWorld(); - - // Temporarily spawn or move player to the center of exterior 0, 0 - ESM::Position spawnPos; - spawnPos.pos[0] = spawnPos.pos[1] = Main::get().getCellController()->getCellSize() / 2; - spawnPos.pos[2] = 0; - MWWorld::CellStore *cellStore = world->getExterior(0, 0); - - LOG_APPEND(Log::LOG_INFO, "- Updating reference pointer for %s", npc.mName.c_str()); - - MWWorld::ESMStore *store = const_cast(&world->getStore()); - MWWorld::Store *creature_store = const_cast *> (&store->get()); - MWWorld::Store *npc_store = const_cast *> (&store->get()); - - if (!creatureRefId.empty()) - { - if (!npc.mId.empty() || npc.mId != "Dedicated Player") - { - LOG_APPEND(Log::LOG_INFO, "- Deleting NPC record"); - npc_store->erase(npc.mId); - npc.mId.clear(); - } - creature.mId = ptr.get()->mBase->mId; - creature_store->insert(creature); - } - else - { - if (!creature.mId.empty() || creature.mId != "Dedicated Player") - { - LOG_APPEND(Log::LOG_INFO, "- Deleting Creature record"); - creature_store->erase(creature.mId); - creature.mId.clear(); - } - npc.mId = ptr.get()->mBase->mId; - npc_store->insert(npc); - } - - // Disable Ptr to avoid graphical glitches caused by race changes - world->disable(ptr); - - setPtr(world->moveObject(ptr, cellStore, spawnPos.pos[0], spawnPos.pos[1], spawnPos.pos[2])); - setCell(); -} - void DedicatedPlayer::deleteReference() { MWBase::World *world = MWBase::Environment::get().getWorld(); - bool isNPC = reference->getPtr().getTypeName() == typeid(ESM::NPC).name(); - if ((!creatureRefId.empty() && isNPC) || - (creatureRefId.empty() && !isNPC)) - { - LOG_APPEND(Log::LOG_INFO, "- Deleting old reference"); - state = 0; - world->deleteObject(ptr); - delete reference; - reference = nullptr; - } + LOG_APPEND(Log::LOG_INFO, "- Deleting reference"); + world->deleteObject(ptr); + delete reference; + reference = nullptr; } MWWorld::Ptr DedicatedPlayer::getPtr() diff --git a/apps/openmw/mwmp/DedicatedPlayer.hpp b/apps/openmw/mwmp/DedicatedPlayer.hpp index b141fa463..d01ac91cb 100644 --- a/apps/openmw/mwmp/DedicatedPlayer.hpp +++ b/apps/openmw/mwmp/DedicatedPlayer.hpp @@ -38,10 +38,10 @@ namespace mwmp void move(float dt); void setBaseInfo(); + void setShapeshift(); void setAnimFlags(); void setEquipment(); void setCell(); - void setShapeshift(); void updateMarker(); void removeMarker(); @@ -53,8 +53,7 @@ namespace mwmp ESM::NPC getNpcRecord(); ESM::Creature getCreatureRecord(); - void createReference(ESM::NPC& npc, ESM::Creature& creature, bool reset); - void updateReference(ESM::NPC& npc, ESM::Creature& creature); + void createReference(ESM::NPC& npc, ESM::Creature& creature); void deleteReference(); MWWorld::Ptr getPtr(); @@ -67,7 +66,6 @@ namespace mwmp DedicatedPlayer(RakNet::RakNetGUID guid); virtual ~DedicatedPlayer(); - int state; MWWorld::ManualRef* reference; MWWorld::Ptr ptr; diff --git a/apps/openmw/mwmp/PlayerList.cpp b/apps/openmw/mwmp/PlayerList.cpp index c7d064d9b..24e392511 100644 --- a/apps/openmw/mwmp/PlayerList.cpp +++ b/apps/openmw/mwmp/PlayerList.cpp @@ -39,30 +39,19 @@ DedicatedPlayer *PlayerList::newPlayer(RakNet::RakNetGUID guid) LOG_APPEND(Log::LOG_INFO, "- Creating new DedicatedPlayer with guid %s", guid.ToString()); players[guid] = new DedicatedPlayer(guid); - players[guid]->state = 0; + + LOG_APPEND(Log::LOG_INFO, "- There are now %i DedicatedPlayers", players.size()); + return players[guid]; } -void PlayerList::disconnectPlayer(RakNet::RakNetGUID guid) +void PlayerList::deletePlayer(RakNet::RakNetGUID guid) { - if (players[guid]->state > 1) - { - players[guid]->state = 1; - - // Remove player's marker - players[guid]->setMarkerState(false); + if (players[guid]->reference) + players[guid]->deleteReference(); - MWBase::World *world = MWBase::Environment::get().getWorld(); - world->disable(players[guid]->getPtr()); - - // Move player to exterior 0,0 - ESM::Position newPos; - newPos.pos[0] = newPos.pos[1] = Main::get().getCellController()->getCellSize() / 2; - newPos.pos[2] = 0; - MWWorld::CellStore *cellStore = world->getExterior(0, 0); - - world->moveObject(players[guid]->getPtr(), cellStore, newPos.pos[0], newPos.pos[1], newPos.pos[2]); - } + delete players[guid]; + players.erase(guid); } void PlayerList::cleanUp() diff --git a/apps/openmw/mwmp/PlayerList.hpp b/apps/openmw/mwmp/PlayerList.hpp index 91f03d869..c8a9eb959 100644 --- a/apps/openmw/mwmp/PlayerList.hpp +++ b/apps/openmw/mwmp/PlayerList.hpp @@ -29,7 +29,7 @@ namespace mwmp static DedicatedPlayer *newPlayer(RakNet::RakNetGUID guid); - static void disconnectPlayer(RakNet::RakNetGUID guid); + static void deletePlayer(RakNet::RakNetGUID guid); static void cleanUp(); static DedicatedPlayer *getPlayer(RakNet::RakNetGUID guid); diff --git a/apps/openmw/mwmp/processors/player/ProcessorUserDisconnected.hpp b/apps/openmw/mwmp/processors/player/ProcessorUserDisconnected.hpp index e1c7fec6d..68eabfda4 100644 --- a/apps/openmw/mwmp/processors/player/ProcessorUserDisconnected.hpp +++ b/apps/openmw/mwmp/processors/player/ProcessorUserDisconnected.hpp @@ -26,7 +26,7 @@ namespace mwmp if (isLocal()) MWBase::Environment::get().getStateManager()->requestQuit(); else if (player != 0) - PlayerList::disconnectPlayer(guid); + PlayerList::deletePlayer(guid); } }; } From c132dc70d2bbd796497ea16939de8dfcdecbc538 Mon Sep 17 00:00:00 2001 From: David Cernat Date: Mon, 9 Apr 2018 15:23:05 +0300 Subject: [PATCH 13/17] [Client] Make PlayerShapeshift turn DedicatedPlayers into creatures --- apps/openmw/mwmp/DedicatedPlayer.cpp | 124 ++++++++++++++------------- apps/openmw/mwmp/DedicatedPlayer.hpp | 6 +- 2 files changed, 67 insertions(+), 63 deletions(-) diff --git a/apps/openmw/mwmp/DedicatedPlayer.cpp b/apps/openmw/mwmp/DedicatedPlayer.cpp index c218f3846..9b6533dbc 100644 --- a/apps/openmw/mwmp/DedicatedPlayer.cpp +++ b/apps/openmw/mwmp/DedicatedPlayer.cpp @@ -134,45 +134,55 @@ void DedicatedPlayer::setBaseInfo() { MWBase::World *world = MWBase::Environment::get().getWorld(); - ESM::Creature tempCreature; - ESM::NPC tempNpc; - - if (!creatureRefId.empty()) - { - const ESM::Creature *tmpCreature = world->getStore().get().search(creatureRefId); - if (tmpCreature == 0) - { - creatureRefId = ""; - } - else - { - tempCreature = getCreatureRecord(); - } - } - - if (creatureRefId.empty()) - { - tempNpc = getNpcRecord(); - } - if (reference) { deleteReference(); } - createReference(tempNpc, tempCreature); + std::string recId = getNpcRecordId(); + createReference(recId); // Give this new character a temporary high fatigue of at least 1 so it doesn't // spawn on the ground creatureStats.mDynamic[2].mBase = 1000; - - world->enable(ptr); } void DedicatedPlayer::setShapeshift() { + MWBase::World *world = MWBase::Environment::get().getWorld(); + + if (reference) + { + deleteReference(); + } + + std::string recId; + + if (!creatureRefId.empty()) + { + const ESM::Creature *tmpCreature = world->getStore().get().search(creatureRefId); + if (tmpCreature != 0) + { + recId = getCreatureRecordId(); + } + } + + if (recId.empty()) + { + recId = getNpcRecordId(); + } + + createReference(recId); + + if (ptr.getTypeName() == typeid(ESM::NPC).name()) + { + MWBase::Environment::get().getMechanicsManager()->setWerewolf(ptr, isWerewolf); + + if (!isWerewolf) + setEquipment(); + } + MWBase::Environment::get().getWorld()->scaleObject(ptr, scale); - MWBase::Environment::get().getMechanicsManager()->setWerewolf(ptr, isWerewolf); } void DedicatedPlayer::setAnimFlags() @@ -340,25 +350,7 @@ void DedicatedPlayer::playSpeech() winMgr->messageBox(MWBase::Environment::get().getDialogueManager()->getVoiceCaption(sound), MWGui::ShowInDialogueMode_Never); } - -ESM::Creature DedicatedPlayer::getCreatureRecord() -{ - MWBase::World *world = MWBase::Environment::get().getWorld(); - - ESM::Creature creature; - - const ESM::Creature *tmpCreature = world->getStore().get().search(creatureRefId); - - creature = *tmpCreature; - creature.mScript = ""; - if (!displayCreatureName) - creature.mName = npc.mName; - LOG_APPEND(Log::LOG_INFO, "Player %s looks like %s", npc.mName.c_str(), creatureRefId.c_str()); - - return creature; -} - -ESM::NPC DedicatedPlayer::getNpcRecord() +std::string DedicatedPlayer::getNpcRecordId() { MWBase::World *world = MWBase::Environment::get().getWorld(); @@ -377,30 +369,42 @@ ESM::NPC DedicatedPlayer::getNpcRecord() newNpc.mName = npc.mName; newNpc.mFlags = npc.mFlags; - return newNpc; + LOG_APPEND(Log::LOG_INFO, "- Creating new NPC record"); + newNpc.mId = "Dedicated Player"; + std::string recId = world->createRecord(newNpc)->mId; + + return recId; } -void DedicatedPlayer::createReference(ESM::NPC& npc, ESM::Creature& creature) +std::string DedicatedPlayer::getCreatureRecordId() { MWBase::World *world = MWBase::Environment::get().getWorld(); - string recId; - if (creatureRefId.empty()) - { - LOG_APPEND(Log::LOG_INFO, "- Creating new NPC record"); - npc.mId = "Dedicated Player"; - recId = world->createRecord(npc)->mId; - } - else - { - LOG_APPEND(Log::LOG_INFO, "- Creating new Creature record"); - creature.mId = "Dedicated Player"; - recId = world->createRecord(creature)->mId; - } + ESM::Creature creature; + + const ESM::Creature *tmpCreature = world->getStore().get().search(creatureRefId); + + creature = *tmpCreature; + creature.mScript = ""; + if (!displayCreatureName) + creature.mName = npc.mName; + LOG_APPEND(Log::LOG_INFO, "Player %s looks like %s", npc.mName.c_str(), creatureRefId.c_str()); + + LOG_APPEND(Log::LOG_INFO, "- Creating new NPC record"); + creature.mId = "Dedicated Player"; + + std::string recId = world->createRecord(creature)->mId; + + return recId; +} + +void DedicatedPlayer::createReference(const std::string& recId) +{ + MWBase::World *world = MWBase::Environment::get().getWorld(); reference = new MWWorld::ManualRef(world->getStore(), recId, 1); - LOG_APPEND(Log::LOG_INFO, "- Creating new reference pointer for %s", this->npc.mName.c_str()); + LOG_APPEND(Log::LOG_INFO, "- Creating new reference pointer for %s", npc.mName.c_str()); ptr = world->placeObject(reference->getPtr(), Main::get().getCellController()->getCellStore(cell), position); diff --git a/apps/openmw/mwmp/DedicatedPlayer.hpp b/apps/openmw/mwmp/DedicatedPlayer.hpp index d01ac91cb..bb3718a0a 100644 --- a/apps/openmw/mwmp/DedicatedPlayer.hpp +++ b/apps/openmw/mwmp/DedicatedPlayer.hpp @@ -50,10 +50,10 @@ namespace mwmp void playAnimation(); void playSpeech(); - ESM::NPC getNpcRecord(); - ESM::Creature getCreatureRecord(); + std::string getNpcRecordId(); + std::string getCreatureRecordId(); - void createReference(ESM::NPC& npc, ESM::Creature& creature); + void createReference(const std::string& recId); void deleteReference(); MWWorld::Ptr getPtr(); From 34893ff6311d6a9543e943ac66c939372a7624de Mon Sep 17 00:00:00 2001 From: David Cernat Date: Mon, 9 Apr 2018 15:25:50 +0300 Subject: [PATCH 14/17] [Server] Split up handshake handling into multiple functions for debug Unfortunately, the handshake attempt limit implemented in 4ebfcc4a21051702425c9f0ad9a710814e7cca90 for 0.7 and then ported over to 0.6 in a3a341fee64843985be9c7a10c105ab65d2ac416 does not appear to work properly, which is why gathering more information on it is important. --- apps/openmw-mp/Networking.cpp | 6 +++++- apps/openmw-mp/Player.cpp | 9 +++++++-- apps/openmw-mp/Player.hpp | 5 +++-- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/apps/openmw-mp/Networking.cpp b/apps/openmw-mp/Networking.cpp index 9c7eb949f..663256dd7 100644 --- a/apps/openmw-mp/Networking.cpp +++ b/apps/openmw-mp/Networking.cpp @@ -116,8 +116,12 @@ void Networking::processPlayerPacket(RakNet::Packet *packet) if (!player->isHandshaked()) { + player->incrementHandshakeAttempts(); LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Have not completed handshake with player %d", player->getId()); - if (player->handshakeAttempts() > 5) + + LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Attempts so far: %i", player->getHandshakeAttempts()); + + if (player->getHandshakeAttempts() > 5) kickPlayer(player->guid); return; } diff --git a/apps/openmw-mp/Player.cpp b/apps/openmw-mp/Player.cpp index 18e51a89a..6e66f8151 100644 --- a/apps/openmw-mp/Player.cpp +++ b/apps/openmw-mp/Player.cpp @@ -100,9 +100,14 @@ void Player::setHandshake() handshakeCounter = numeric_limits::max(); } -int Player::handshakeAttempts() +void Player::incrementHandshakeAttempts() { - return handshakeCounter++; + handshakeCounter++; +} + +int Player::getHandshakeAttempts() +{ + return handshakeCounter; } diff --git a/apps/openmw-mp/Player.hpp b/apps/openmw-mp/Player.hpp index aa52d7574..a956a4a52 100644 --- a/apps/openmw-mp/Player.hpp +++ b/apps/openmw-mp/Player.hpp @@ -58,7 +58,8 @@ public: void setId(unsigned short id); bool isHandshaked(); - int handshakeAttempts(); + int getHandshakeAttempts(); + void incrementHandshakeAttempts(); void setHandshake(); void setLoadState(int state); @@ -73,8 +74,8 @@ public: private: CellController::TContainer cells; - int handshakeCounter; int loadState; + int handshakeCounter; }; From cddc0914c87df67a987cf40caeae07913851ee2e Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Mon, 9 Apr 2018 01:00:40 +0300 Subject: [PATCH 15/17] Option to charge for all companions travelling (fixes #4064) --- apps/openmw/mwgui/travelwindow.cpp | 7 ++++++- files/settings-default.cfg | 3 +++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/travelwindow.cpp b/apps/openmw/mwgui/travelwindow.cpp index 7586bb66a..7b65eb771 100644 --- a/apps/openmw/mwgui/travelwindow.cpp +++ b/apps/openmw/mwgui/travelwindow.cpp @@ -4,6 +4,8 @@ #include #include +#include + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" @@ -72,7 +74,10 @@ namespace MWGui MWWorld::ActionTeleport::getFollowersToTeleport(player, followers); // Apply followers cost, in vanilla one follower travels for free - price *= std::max(1, static_cast(followers.size())); + if (Settings::Manager::getBool("charge for every follower travelling", "Game")) + price *= 1 + static_cast(followers.size()); + else + price *= std::max(1, static_cast(followers.size())); MyGUI::Button* toAdd = mDestinationsView->createWidget("SandTextButton", 0, mCurrentY, 200, sLineHeight, MyGUI::Align::Default); toAdd->setEnabled(price <= playerGold); diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 8a28c42e4..46d9934fd 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -176,6 +176,9 @@ difficulty = 0 # Show duration of magic effect and lights in the spells window. show effect duration = false +# Account for the first follower in fast travel cost calculations. +charge for every follower travelling = false + # Prevents merchants from equipping items that are sold to them. prevent merchant equipping = false From 73dea494c4b628ea5d8154a49a97908167563507 Mon Sep 17 00:00:00 2001 From: David Cernat Date: Mon, 9 Apr 2018 19:21:19 +0300 Subject: [PATCH 16/17] [General] Allow changes from PlayerBaseInfo without player stat reset --- apps/openmw/mwmp/LocalPlayer.cpp | 41 +++++++++++++++---- apps/openmw/mwmp/LocalPlayer.hpp | 2 +- .../player/ProcessorPlayerBaseInfo.hpp | 4 +- components/openmw-mp/Base/BasePlayer.hpp | 2 + 4 files changed, 39 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwmp/LocalPlayer.cpp b/apps/openmw/mwmp/LocalPlayer.cpp index 15ffd0707..214821eda 100644 --- a/apps/openmw/mwmp/LocalPlayer.cpp +++ b/apps/openmw/mwmp/LocalPlayer.cpp @@ -440,13 +440,6 @@ void LocalPlayer::updateCell(bool forceUpdate) } } -void LocalPlayer::updateChar() -{ - MWBase::Environment::get().getMechanicsManager()->setPlayerRace(npc.mRace, npc.isMale(), npc.mHead, npc.mHair); - MWBase::Environment::get().getMechanicsManager()->setPlayerBirthsign(birthsign); - MWBase::Environment::get().getWindowManager()->getInventoryWindow()->rebuildAvatar(); -} - void LocalPlayer::updateEquipment(bool forceUpdate) { MWWorld::Ptr ptrPlayer = getPlayerPtr(); @@ -838,6 +831,40 @@ void LocalPlayer::closeInventoryWindows() MWBase::Environment::get().getWindowManager()->finishDragDrop(); } +void LocalPlayer::setCharacter() +{ + MWBase::World *world = MWBase::Environment::get().getWorld(); + + // Ignore invalid races + if (world->getStore().get().search(npc.mRace) != 0) + { + MWBase::Environment::get().getWorld()->getPlayer().setBirthSign(birthsign); + + if (resetStats) + MWBase::Environment::get().getMechanicsManager()->setPlayerRace(npc.mRace, npc.isMale(), npc.mHead, npc.mHair); + else + { + ESM::NPC player = *world->getPlayerPtr().get()->mBase; + + player.mRace = npc.mRace; + player.mHead = npc.mHead; + player.mHair = npc.mHair; + player.setIsMale(npc.isMale()); + world->createRecord(player); + + MWBase::Environment::get().getMechanicsManager()->playerLoaded(); + } + + setEquipment(); + + MWBase::Environment::get().getWindowManager()->getInventoryWindow()->rebuildAvatar(); + } + else + { + LOG_APPEND(Log::LOG_INFO, "- Character update was ignored due to invalid race %s", npc.mRace.c_str()); + } +} + void LocalPlayer::setDynamicStats() { MWBase::World *world = MWBase::Environment::get().getWorld(); diff --git a/apps/openmw/mwmp/LocalPlayer.hpp b/apps/openmw/mwmp/LocalPlayer.hpp index bd475977d..44baea429 100644 --- a/apps/openmw/mwmp/LocalPlayer.hpp +++ b/apps/openmw/mwmp/LocalPlayer.hpp @@ -33,7 +33,6 @@ namespace mwmp void updateReputation(bool forceUpdate = false); void updatePosition(bool forceUpdate = false); void updateCell(bool forceUpdate = false); - void updateChar(); void updateEquipment(bool forceUpdate = false); void updateInventory(bool forceUpdate = false); void updateAttack(); @@ -52,6 +51,7 @@ namespace mwmp void closeInventoryWindows(); + void setCharacter(); void setDynamicStats(); void setAttributes(); void setSkills(); diff --git a/apps/openmw/mwmp/processors/player/ProcessorPlayerBaseInfo.hpp b/apps/openmw/mwmp/processors/player/ProcessorPlayerBaseInfo.hpp index 026a1ca1e..eab9e8182 100644 --- a/apps/openmw/mwmp/processors/player/ProcessorPlayerBaseInfo.hpp +++ b/apps/openmw/mwmp/processors/player/ProcessorPlayerBaseInfo.hpp @@ -32,8 +32,8 @@ namespace mwmp } else { - LOG_APPEND(Log::LOG_INFO, "- Updating LocalPlayer"); - static_cast(player)->updateChar(); + LOG_APPEND(Log::LOG_INFO, "- Setting character for LocalPlayer"); + static_cast(player)->setCharacter(); } } else diff --git a/components/openmw-mp/Base/BasePlayer.hpp b/components/openmw-mp/Base/BasePlayer.hpp index 0544f3fb1..9186a383c 100644 --- a/components/openmw-mp/Base/BasePlayer.hpp +++ b/components/openmw-mp/Base/BasePlayer.hpp @@ -225,6 +225,7 @@ namespace mwmp spellbookChanges.action = 0; spellbookChanges.count = 0; displayCreatureName = false; + resetStats = false; } BasePlayer() @@ -285,6 +286,7 @@ namespace mwmp std::string sound; Animation animation; + bool resetStats; float scale; bool isWerewolf; From bdc9132e7ccc6e52faba9487a15f59158e50a85f Mon Sep 17 00:00:00 2001 From: David Cernat Date: Mon, 9 Apr 2018 19:24:24 +0300 Subject: [PATCH 17/17] [General] Add SetResetStats server script function Add reading and writing of resetStats variable to PlayerBaseInfo Packet Fix typos in various server script function descriptions --- apps/openmw-mp/Script/Functions/Mechanics.hpp | 4 ++-- apps/openmw-mp/Script/Functions/Settings.hpp | 14 +++++++------- apps/openmw-mp/Script/Functions/Stats.cpp | 8 ++++++++ apps/openmw-mp/Script/Functions/Stats.hpp | 14 ++++++++++++++ .../Packets/Player/PacketPlayerBaseInfo.cpp | 2 ++ 5 files changed, 33 insertions(+), 9 deletions(-) diff --git a/apps/openmw-mp/Script/Functions/Mechanics.hpp b/apps/openmw-mp/Script/Functions/Mechanics.hpp index 0ab87c303..24b91344c 100644 --- a/apps/openmw-mp/Script/Functions/Mechanics.hpp +++ b/apps/openmw-mp/Script/Functions/Mechanics.hpp @@ -201,7 +201,7 @@ public: * does not by itself send a packet. * * \param pid The player ID. - * \param bool The new scale. + * \param scale The new scale. * \return void */ static void SetScale(unsigned short pid, double scale) noexcept; @@ -213,7 +213,7 @@ public: * does not by itself send a packet. * * \param pid The player ID. - * \param bool The new werewolf state. + * \param isWerewolf The new werewolf state. * \return void */ static void SetWerewolfState(unsigned short pid, bool isWerewolf) noexcept; diff --git a/apps/openmw-mp/Script/Functions/Settings.hpp b/apps/openmw-mp/Script/Functions/Settings.hpp index 48437028d..1d5275c58 100644 --- a/apps/openmw-mp/Script/Functions/Settings.hpp +++ b/apps/openmw-mp/Script/Functions/Settings.hpp @@ -26,7 +26,7 @@ public: * send a packet. * * \param pid The player ID. - * \param bool The difficulty. + * \param difficulty The difficulty. * \return void */ static void SetDifficulty(unsigned short pid, int difficulty); @@ -44,7 +44,7 @@ public: * If you do not wish to enforce a log level, simply set enforcedLogLevel to -1 * * \param pid The player ID. - * \param bool The enforced log level. + * \param enforcedLogLevel The enforced log level. * \return void */ static void SetEnforcedLogLevel(unsigned short pid, int enforcedLogLevel); @@ -56,7 +56,7 @@ public: * send a packet. * * \param pid The player ID. - * \param bool The physics framerate. + * \param physicsFramerate The physics framerate. * \return void */ static void SetPhysicsFramerate(unsigned short pid, double physicsFramerate); @@ -68,7 +68,7 @@ public: * by itself send a packet. * * \param pid The player ID. - * \param bool The console permission state. + * \param state The console permission state. * \return void */ static void SetConsoleAllowed(unsigned short pid, bool state); @@ -80,7 +80,7 @@ public: * by itself send a packet. * * \param pid The player ID. - * \param bool The resting permission state. + * \param state The resting permission state. * \return void */ static void SetBedRestAllowed(unsigned short pid, bool state); @@ -92,7 +92,7 @@ public: * by itself send a packet. * * \param pid The player ID. - * \param bool The resting permission state. + * \param state The resting permission state. * \return void */ static void SetWildernessRestAllowed(unsigned short pid, bool state); @@ -104,7 +104,7 @@ public: * by itself send a packet. * * \param pid The player ID. - * \param bool The waiting permission state. + * \param state The waiting permission state. * \return void */ static void SetWaitAllowed(unsigned short pid, bool state); diff --git a/apps/openmw-mp/Script/Functions/Stats.cpp b/apps/openmw-mp/Script/Functions/Stats.cpp index 32b22144a..c39e3f021 100644 --- a/apps/openmw-mp/Script/Functions/Stats.cpp +++ b/apps/openmw-mp/Script/Functions/Stats.cpp @@ -330,6 +330,14 @@ void StatsFunctions::SetBirthsign(unsigned short pid, const char *sign) noexcept player->birthsign = sign; } +void StatsFunctions::SetResetStats(unsigned short pid, bool resetStats) noexcept +{ + Player *player; + GET_PLAYER(pid, player, ); + + player->resetStats = resetStats; +} + void StatsFunctions::SetLevel(unsigned short pid, int value) noexcept { Player *player; diff --git a/apps/openmw-mp/Script/Functions/Stats.hpp b/apps/openmw-mp/Script/Functions/Stats.hpp index 3d0da464d..f77b44909 100644 --- a/apps/openmw-mp/Script/Functions/Stats.hpp +++ b/apps/openmw-mp/Script/Functions/Stats.hpp @@ -49,6 +49,7 @@ {"SetHair", StatsFunctions::SetHairstyle},\ {"SetIsMale", StatsFunctions::SetIsMale},\ {"SetBirthsign", StatsFunctions::SetBirthsign},\ + {"SetResetStats", StatsFunctions::SetResetStats},\ \ {"SetLevel", StatsFunctions::SetLevel},\ {"SetLevelProgress", StatsFunctions::SetLevelProgress},\ @@ -123,6 +124,19 @@ public: static void SetHairstyle(unsigned short pid, const char *style) noexcept; static void SetIsMale(unsigned short pid, int male) noexcept; static void SetBirthsign(unsigned short pid, const char *name) noexcept; + + /** + * \brief Set whether the player's stats should be reset based on their + * current race as the result of a PlayerBaseInfo packet. + * + * This changes the resetState for that player in the server memory, but does not by itself + * send a packet. + * + * \param pid The player ID. + * \param resetStats The stat reset state. + * \return void + */ + static void SetResetStats(unsigned short pid, bool resetStats) noexcept; static void SetLevel(unsigned short pid, int value) noexcept; static void SetLevelProgress(unsigned short pid, int value) noexcept; diff --git a/components/openmw-mp/Packets/Player/PacketPlayerBaseInfo.cpp b/components/openmw-mp/Packets/Player/PacketPlayerBaseInfo.cpp index 051c26e83..6247c4bd4 100644 --- a/components/openmw-mp/Packets/Player/PacketPlayerBaseInfo.cpp +++ b/components/openmw-mp/Packets/Player/PacketPlayerBaseInfo.cpp @@ -25,4 +25,6 @@ void PacketPlayerBaseInfo::Packet(RakNet::BitStream *bs, bool send) RW(player->npc.mFlags, send); RW(player->birthsign, send, 1); + + RW(player->resetStats, send); }