From 2b90504558ec7861a5f0ed6d36339a29772663ba Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 7 Aug 2018 22:57:16 +0400 Subject: [PATCH 01/53] Init SDL2 before Qt4 to avoid crash on Linux (bug #4529) --- apps/launcher/CMakeLists.txt | 2 ++ apps/launcher/graphicspage.cpp | 23 ++++++----------------- apps/launcher/graphicspage.hpp | 7 ++----- apps/launcher/main.cpp | 18 ++++++++++++++++-- apps/launcher/sdlinit.cpp | 25 +++++++++++++++++++++++++ apps/launcher/sdlinit.hpp | 8 ++++++++ 6 files changed, 59 insertions(+), 24 deletions(-) create mode 100644 apps/launcher/sdlinit.cpp create mode 100644 apps/launcher/sdlinit.hpp diff --git a/apps/launcher/CMakeLists.txt b/apps/launcher/CMakeLists.txt index ec2e963d1..bfc08a7d6 100644 --- a/apps/launcher/CMakeLists.txt +++ b/apps/launcher/CMakeLists.txt @@ -1,6 +1,7 @@ set(LAUNCHER datafilespage.cpp graphicspage.cpp + sdlinit.cpp main.cpp maindialog.cpp playpage.cpp @@ -19,6 +20,7 @@ set(LAUNCHER set(LAUNCHER_HEADER datafilespage.hpp graphicspage.hpp + sdlinit.hpp maindialog.hpp playpage.hpp textslotmsgbox.hpp diff --git a/apps/launcher/graphicspage.cpp b/apps/launcher/graphicspage.cpp index 34442fe71..072f1f36f 100644 --- a/apps/launcher/graphicspage.cpp +++ b/apps/launcher/graphicspage.cpp @@ -12,7 +12,6 @@ #define MAC_OS_X_VERSION_MIN_REQUIRED __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ #endif // MAC_OS_X_VERSION_MIN_REQUIRED -#include #include #include @@ -48,27 +47,15 @@ Launcher::GraphicsPage::GraphicsPage(Files::ConfigurationManager &cfg, Settings: } -bool Launcher::GraphicsPage::connectToSdl() { - SDL_SetHint(SDL_HINT_RENDER_DRIVER, "software"); - SDL_SetMainReady(); - // Required for determining screen resolution and such on the Graphics tab - if (SDL_Init(SDL_INIT_VIDEO) != 0) - { - return false; - } - signal(SIGINT, SIG_DFL); // We don't want to use the SDL event loop in the launcher, - // so reset SIGINT which SDL wants to redirect to an SDL_Quit event. - - return true; -} - bool Launcher::GraphicsPage::setupSDL() { - bool sdlConnectSuccessful = connectToSdl(); +#if QT_VERSION >= QT_VERSION_CHECK(5,0,0) + bool sdlConnectSuccessful = initSDL(); if (!sdlConnectSuccessful) { return false; } +#endif int displays = SDL_GetNumVideoDisplays(); @@ -89,8 +76,10 @@ bool Launcher::GraphicsPage::setupSDL() screenComboBox->addItem(QString(tr("Screen ")) + QString::number(i + 1)); } +#if QT_VERSION >= QT_VERSION_CHECK(5,0,0) // Disconnect from SDL processes - SDL_Quit(); + quitSDL(); +#endif return true; } diff --git a/apps/launcher/graphicspage.hpp b/apps/launcher/graphicspage.hpp index 0354e5202..9d943d5e2 100644 --- a/apps/launcher/graphicspage.hpp +++ b/apps/launcher/graphicspage.hpp @@ -7,6 +7,8 @@ #include +#include "sdlinit.hpp" + namespace Files { struct ConfigurationManager; } namespace Launcher @@ -37,11 +39,6 @@ namespace Launcher QStringList getAvailableResolutions(int screen); QRect getMaximumResolution(); - /** - * Connect to the SDL so that we can use it to determine graphics - * @return whether or not connecting to SDL is successful - */ - bool connectToSdl(); bool setupSDL(); }; } diff --git a/apps/launcher/main.cpp b/apps/launcher/main.cpp index 866ae2aa9..c4cf568c4 100644 --- a/apps/launcher/main.cpp +++ b/apps/launcher/main.cpp @@ -12,11 +12,18 @@ #endif // MAC_OS_X_VERSION_MIN_REQUIRED #include "maindialog.hpp" +#include "sdlinit.hpp" int main(int argc, char *argv[]) { try { +// Note: we should init SDL2 before Qt4 to avoid crashes on Linux, +// but we should init SDL2 after Qt5 to avoid input issues on MacOS X. +#if QT_VERSION < QT_VERSION_CHECK(5,0,0) + initSDL(); +#endif + QApplication app(argc, argv); // Now we make sure the current dir is set to application path @@ -33,11 +40,18 @@ int main(int argc, char *argv[]) if (result == Launcher::FirstRunDialogResultContinue) mainWin.show(); - return app.exec(); + int exitCode = app.exec(); + +#if QT_VERSION < QT_VERSION_CHECK(5,0,0) + // Disconnect from SDL processes + quitSDL(); +#endif + + return exitCode; } catch (std::exception& e) { std::cerr << "ERROR: " << e.what() << std::endl; return 0; } -} \ No newline at end of file +} diff --git a/apps/launcher/sdlinit.cpp b/apps/launcher/sdlinit.cpp new file mode 100644 index 000000000..1fe1fd4c2 --- /dev/null +++ b/apps/launcher/sdlinit.cpp @@ -0,0 +1,25 @@ +#include + +#include +#include + +bool initSDL() +{ + SDL_SetHint(SDL_HINT_RENDER_DRIVER, "software"); + SDL_SetMainReady(); + // Required for determining screen resolution and such on the Graphics tab + if (SDL_Init(SDL_INIT_VIDEO) != 0) + { + return false; + } + signal(SIGINT, SIG_DFL); // We don't want to use the SDL event loop in the launcher, + // so reset SIGINT which SDL wants to redirect to an SDL_Quit event. + + return true; +} + +void quitSDL() +{ + // Disconnect from SDL processes + SDL_Quit(); +} diff --git a/apps/launcher/sdlinit.hpp b/apps/launcher/sdlinit.hpp new file mode 100644 index 000000000..e6718f0e9 --- /dev/null +++ b/apps/launcher/sdlinit.hpp @@ -0,0 +1,8 @@ +#ifndef SDLINIT_H +#define SDLINIT_H + +bool initSDL(); + +void quitSDL(); + +#endif From bcd9cc4baa06c70150e0ce31eecc418da7db6ff5 Mon Sep 17 00:00:00 2001 From: Capostrophic <21265616+Capostrophic@users.noreply.github.com> Date: Wed, 8 Aug 2018 02:05:12 +0300 Subject: [PATCH 02/53] Check the actor cell instead of the destination cell in fast travel price logic --- CHANGELOG.md | 1 + apps/openmw/mwgui/travelwindow.cpp | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a28661372..bb3c6bb7d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -81,6 +81,7 @@ Bug #4553: Forcegreeting on non-actor opens a dialogue window which cannot be closed Bug #4557: Topics with reserved names are handled differently from vanilla Bug #4558: Mesh optimizer: check for reserved node name is case-sensitive + Bug #4563: Fast travel price logic checks destination cell instead of service actor cell Feature #2606: Editor: Implemented (optional) case sensitive global search Feature #3083: Play animation when NPC is casting spell via script Feature #3103: Provide option for disposition to get increased by successful trade diff --git a/apps/openmw/mwgui/travelwindow.cpp b/apps/openmw/mwgui/travelwindow.cpp index 7b65eb771..991b93a62 100644 --- a/apps/openmw/mwgui/travelwindow.cpp +++ b/apps/openmw/mwgui/travelwindow.cpp @@ -46,7 +46,7 @@ namespace MWGui mSelect->getHeight()); } - void TravelWindow::addDestination(const std::string& name,ESM::Position pos,bool interior) + void TravelWindow::addDestination(const std::string& name, ESM::Position pos, bool interior) { int price; @@ -56,7 +56,7 @@ namespace MWGui MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); int playerGold = player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId); - if(interior) + if (!mPtr.getCell()->isExterior()) { price = gmst.find("fMagesGuildTravel")->getInt(); } From b7859b3fa979ca7087226392cade5eaf713f86a5 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 7 Aug 2018 23:44:21 +0400 Subject: [PATCH 03/53] Cap underwater view distance (bug #4565) --- CHANGELOG.md | 1 + apps/openmw/mwrender/renderingmanager.cpp | 6 ++---- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a28661372..17058b25e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -81,6 +81,7 @@ Bug #4553: Forcegreeting on non-actor opens a dialogue window which cannot be closed Bug #4557: Topics with reserved names are handled differently from vanilla Bug #4558: Mesh optimizer: check for reserved node name is case-sensitive + Bug #4565: Underwater view distance should be limited Feature #2606: Editor: Implemented (optional) case sensitive global search Feature #3083: Play animation when NPC is casting spell via script Feature #3103: Provide option for disposition to get increased by successful trade diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index f6aa8796d..03863000f 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -554,8 +554,8 @@ namespace MWRender mLandFogStart = mViewDistance * (1 - fogDepth); mLandFogEnd = mViewDistance; } - mUnderwaterFogStart = mViewDistance * (1 - underwaterFog); - mUnderwaterFogEnd = mViewDistance; + mUnderwaterFogStart = std::min(mViewDistance, 6666.f) * (1 - underwaterFog); + mUnderwaterFogEnd = std::min(mViewDistance, 6666.f); } mFogColor = color; } @@ -585,8 +585,6 @@ namespace MWRender mCurrentCameraPos = cameraPos; if (mWater->isUnderwater(cameraPos)) { - float viewDistance = mViewDistance; - viewDistance = std::min(viewDistance, 6666.f); setFogColor(mUnderwaterColor * mUnderwaterWeight + mFogColor * (1.f-mUnderwaterWeight)); mStateUpdater->setFogStart(mUnderwaterFogStart); mStateUpdater->setFogEnd(mUnderwaterFogEnd); From e9e9c0dd6b3873fc8968b11279032072c1a2de2d Mon Sep 17 00:00:00 2001 From: Capostrophic <21265616+Capostrophic@users.noreply.github.com> Date: Wed, 8 Aug 2018 12:27:18 +0300 Subject: [PATCH 04/53] Fix guild guide fast travelling to exteriors time --- apps/openmw/mwgui/travelwindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/travelwindow.cpp b/apps/openmw/mwgui/travelwindow.cpp index 991b93a62..7a4a9293c 100644 --- a/apps/openmw/mwgui/travelwindow.cpp +++ b/apps/openmw/mwgui/travelwindow.cpp @@ -168,7 +168,7 @@ namespace MWGui ESM::Position pos = *_sender->getUserData(); std::string cellname = _sender->getUserString("Destination"); bool interior = _sender->getUserString("interior") == "y"; - if (!interior) + if (mPtr.getCell()->isExterior()) { ESM::Position playerPos = player.getRefData().getPosition(); float d = (osg::Vec3f(pos.pos[0], pos.pos[1], 0) - osg::Vec3f(playerPos.pos[0], playerPos.pos[1], 0)).length(); From 85208eff7f1bd938038042e80fbd6fd428c12f0a Mon Sep 17 00:00:00 2001 From: Capostrophic <21265616+Capostrophic@users.noreply.github.com> Date: Wed, 8 Aug 2018 21:06:12 +0300 Subject: [PATCH 05/53] Revert untextured shapes rendering changes --- CHANGELOG.md | 1 - components/nifosg/nifloader.cpp | 4 ---- 2 files changed, 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a28661372..9bacf11f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -65,7 +65,6 @@ Bug #4475: Scripted animations should not cause movement Bug #4479: "Game" category on Advanced page is getting too long Bug #4480: Segfault in QuickKeysMenu when item no longer in inventory - Bug #4483: Shapes without NiTexturingProperty are rendered Bug #4489: Goodbye doesn't block dialogue hyperlinks Bug #4490: PositionCell on player gives "Error: tried to add local script twice" Bug #4494: Training cap based off Base Skill instead of Modified Skill diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 6d2a01f5d..4e7f6d511 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -573,10 +573,6 @@ namespace NifOsg } } - // Make sure we don't render untextured shapes - if (nifNode->recType == Nif::RC_NiTriShape && boundTextures.empty()) - node->setNodeMask(0x0); - if(nifNode->recType == Nif::RC_NiAutoNormalParticles || nifNode->recType == Nif::RC_NiRotatingParticles) handleParticleSystem(nifNode, node, composite, animflags, rootNode); From 6202b4eca93bc7db9df0768d3d6da110ec5a9e66 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 7 Aug 2018 19:17:38 +0400 Subject: [PATCH 06/53] Do not touch GUI modes when taking screenshots (bug #4528) --- apps/openmw/mwgui/loadingscreen.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp index 3df482d68..28f4b8890 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -170,12 +170,18 @@ namespace MWGui // We are already using node masks to avoid the scene from being updated/rendered, but node masks don't work for computeBound() mViewer->getSceneData()->setComputeBoundingSphereCallback(new DontComputeBoundCallback); + mShowWallpaper = visible && (MWBase::Environment::get().getStateManager()->getState() + == MWBase::StateManager::State_NoGame); + + if (!visible) + { + draw(); + return; + } + mVisible = visible; mLoadingBox->setVisible(mVisible); - mShowWallpaper = mVisible && (MWBase::Environment::get().getStateManager()->getState() - == MWBase::StateManager::State_NoGame); - setVisible(true); if (mShowWallpaper) @@ -184,9 +190,6 @@ namespace MWGui } MWBase::Environment::get().getWindowManager()->pushGuiMode(mShowWallpaper ? GM_LoadingWallpaper : GM_Loading); - - if (!mVisible) - draw(); } void LoadingScreen::loadingOff() From 51af72930549ac5fd71382e4329dd8cbd7ecb678 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 8 Aug 2018 23:21:20 +0400 Subject: [PATCH 07/53] Do not use headtracking in the 1st-person view (bug #4573) --- CHANGELOG.md | 1 + apps/openmw/mwmechanics/actors.cpp | 9 ++++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a28661372..0b35016ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -81,6 +81,7 @@ Bug #4553: Forcegreeting on non-actor opens a dialogue window which cannot be closed Bug #4557: Topics with reserved names are handled differently from vanilla Bug #4558: Mesh optimizer: check for reserved node name is case-sensitive + Bug #4573: Player uses headtracking in the 1st-person mode Feature #2606: Editor: Implemented (optional) case sensitive global search Feature #3083: Play animation when NPC is casting spell via script Feature #3103: Provide option for disposition to get increased by successful trade diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 768d9e37a..5b9782e17 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -1353,12 +1353,15 @@ namespace MWMechanics MWWorld::Ptr headTrackTarget; MWMechanics::CreatureStats& stats = iter->first.getClass().getCreatureStats(iter->first); + bool firstPersonPlayer = iter->first == player && MWBase::Environment::get().getWorld()->isFirstPerson(); - // Unconsious actor can not track target - // Also actors in combat and pursue mode do not bother to headtrack + // 1. Unconsious actor can not track target + // 2. Actors in combat and pursue mode do not bother to headtrack + // 3. Player character does not use headtracking in the 1st-person view if (!stats.getKnockedDown() && !stats.getAiSequence().isInCombat() && - !stats.getAiSequence().hasPackage(AiPackage::TypeIdPursue)) + !stats.getAiSequence().hasPackage(AiPackage::TypeIdPursue) && + !firstPersonPlayer) { for(PtrActorMap::iterator it(mActors.begin()); it != mActors.end(); ++it) { From 348c6f848eac18463e728e15b7520c82ffecac10 Mon Sep 17 00:00:00 2001 From: Capostrophic <21265616+Capostrophic@users.noreply.github.com> Date: Thu, 9 Aug 2018 02:27:33 +0300 Subject: [PATCH 08/53] Fix a bunch of MSVC warnings --- components/misc/debugging.hpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/components/misc/debugging.hpp b/components/misc/debugging.hpp index a983a88df..c0c3f5a17 100644 --- a/components/misc/debugging.hpp +++ b/components/misc/debugging.hpp @@ -76,12 +76,8 @@ int wrapApplication(int (*innerApplication)(int argc, char *argv[]), int argc, c std::cerr.rdbuf (&sb); #else // Redirect cout and cerr to the log file - boost::filesystem::ofstream logfile; logfile.open (boost::filesystem::path(cfgMgr.getLogPath() / logName)); - std::ostream oldcout(cout_rdbuf); - std::ostream oldcerr(cerr_rdbuf); - coutsb.open (Misc::Tee(logfile, oldcout)); cerrsb.open (Misc::Tee(logfile, oldcerr)); From 57e1462417af48daad864aa6408cad0f3c1bdb30 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Thu, 9 Aug 2018 11:01:23 +0400 Subject: [PATCH 09/53] Do not use fall-through --- extern/oics/tinyxmlparser.cpp | 30 +++++++++++------------------- 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/extern/oics/tinyxmlparser.cpp b/extern/oics/tinyxmlparser.cpp index c29e263f4..241f85c3a 100644 --- a/extern/oics/tinyxmlparser.cpp +++ b/extern/oics/tinyxmlparser.cpp @@ -104,25 +104,17 @@ void TiXmlBase::ConvertUTF32ToUTF8( unsigned long input, char* output, int* leng output += *length; - // Scary scary fall throughs. - switch (*length) - { - case 4: - --output; - *output = (char)((input | BYTE_MARK) & BYTE_MASK); - input >>= 6; - case 3: - --output; - *output = (char)((input | BYTE_MARK) & BYTE_MASK); - input >>= 6; - case 2: - --output; - *output = (char)((input | BYTE_MARK) & BYTE_MASK); - input >>= 6; - case 1: - --output; - *output = (char)(input | FIRST_BYTE_MARK[*length]); - } + int lengthLeft = *length; + while (lengthLeft > 1) + { + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + --lengthLeft; + } + + --output; + *output = (char)(input | FIRST_BYTE_MARK[*length]); } From 126b2fdd42836d0c62afc4e9374e4123669cd2c4 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Thu, 9 Aug 2018 11:16:19 +0400 Subject: [PATCH 10/53] Use the isPlayer variable to do not check if the current actor is player every time --- apps/openmw/mwmechanics/actors.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 5b9782e17..e3cd51799 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -1298,6 +1298,8 @@ namespace MWMechanics // AI and magic effects update for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) { + bool isPlayer = iter->first == player; + float distSqr = (player.getRefData().getPosition().asVec3() - iter->first.getRefData().getPosition().asVec3()).length2(); // AI processing is only done within distance of 7168 units to the player. Note the "AI distance" slider doesn't affect this // (it only does some throttling for targets beyond the "AI distance", so doesn't give any guarantees as to whether AI will be enabled or not) @@ -1305,7 +1307,7 @@ namespace MWMechanics // using higher values will make a quest in Bloodmoon harder or impossible to complete (bug #1876) bool inProcessingRange = distSqr <= sqrAiProcessingDistance; - if (iter->first == player) + if (isPlayer) iter->second->getCharacterController()->setAttackingOrSpell(MWBase::Environment::get().getWorld()->getPlayer().getAttackingOrSpell()); // If dead or no longer in combat, no longer store any actors who attempted to hit us. Also remove for the player. @@ -1337,14 +1339,14 @@ namespace MWMechanics { if (timerUpdateAITargets == 0) { - if (iter->first != player) + if (!isPlayer) adjustCommandedActor(iter->first); for(PtrActorMap::iterator it(mActors.begin()); it != mActors.end(); ++it) { - if (it->first == iter->first || iter->first == player) // player is not AI-controlled + if (it->first == iter->first || isPlayer) // player is not AI-controlled continue; - engageCombat(iter->first, it->first, cachedAllies, it->first == player); + engageCombat(iter->first, it->first, cachedAllies, isPlayer); } } if (timerUpdateHeadTrack == 0) @@ -1353,7 +1355,7 @@ namespace MWMechanics MWWorld::Ptr headTrackTarget; MWMechanics::CreatureStats& stats = iter->first.getClass().getCreatureStats(iter->first); - bool firstPersonPlayer = iter->first == player && MWBase::Environment::get().getWorld()->isFirstPerson(); + bool firstPersonPlayer = isPlayer && MWBase::Environment::get().getWorld()->isFirstPerson(); // 1. Unconsious actor can not track target // 2. Actors in combat and pursue mode do not bother to headtrack From cd92014533a83f0abcfe44dd9294ce7dd53f32f4 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 7 Aug 2018 19:17:38 +0400 Subject: [PATCH 11/53] Do not touch GUI modes when taking screenshots (bug #4528) --- apps/openmw/mwgui/loadingscreen.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp index 3df482d68..28f4b8890 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -170,12 +170,18 @@ namespace MWGui // We are already using node masks to avoid the scene from being updated/rendered, but node masks don't work for computeBound() mViewer->getSceneData()->setComputeBoundingSphereCallback(new DontComputeBoundCallback); + mShowWallpaper = visible && (MWBase::Environment::get().getStateManager()->getState() + == MWBase::StateManager::State_NoGame); + + if (!visible) + { + draw(); + return; + } + mVisible = visible; mLoadingBox->setVisible(mVisible); - mShowWallpaper = mVisible && (MWBase::Environment::get().getStateManager()->getState() - == MWBase::StateManager::State_NoGame); - setVisible(true); if (mShowWallpaper) @@ -184,9 +190,6 @@ namespace MWGui } MWBase::Environment::get().getWindowManager()->pushGuiMode(mShowWallpaper ? GM_LoadingWallpaper : GM_Loading); - - if (!mVisible) - draw(); } void LoadingScreen::loadingOff() From 6a03aa6fdb34cf2bf4d440780fc87471629b810f Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Thu, 12 Jul 2018 12:43:49 +0400 Subject: [PATCH 12/53] Reduce jittering during turning animations for player --- CHANGELOG.md | 1 + apps/openmw/mwmechanics/character.cpp | 34 +++++++++++++++++++++------ 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a6621bf2..1f3c0f7ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -83,6 +83,7 @@ Bug #4563: Fast travel price logic checks destination cell instead of service actor cell Bug #4565: Underwater view distance should be limited Bug #4573: Player uses headtracking in the 1st-person mode + Bug #4574: Player turning animations are twitchy Feature #2606: Editor: Implemented (optional) case sensitive global search Feature #3083: Play animation when NPC is casting spell via script Feature #3103: Provide option for disposition to get increased by successful trade diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index f5d6f8584..054f0b64d 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1992,13 +1992,31 @@ void CharacterController::update(float duration) } } - mTurnAnimationThreshold -= duration; - if (isTurning()) - mTurnAnimationThreshold = 0.05f; - else if (movestate == CharState_None && isTurning() - && mTurnAnimationThreshold > 0) + // Player can not use smooth turning as NPCs, so we play turning animation a bit to avoid jittering + if (mPtr == getPlayer()) + { + float threshold = mCurrentMovement.find("swim") == std::string::npos ? 0.4f : 0.8f; + float complete; + bool animPlaying = mAnimation->getInfo(mCurrentMovement, &complete); + if (movestate == CharState_None && isTurning()) + { + if ((animPlaying && complete < threshold) || mJumpState != jumpstate) + movestate = mMovementState; + } + } + else { - movestate = mMovementState; + mTurnAnimationThreshold -= duration; + if (movestate == CharState_TurnRight || movestate == CharState_TurnLeft || + movestate == CharState_SwimTurnRight || movestate == CharState_SwimTurnLeft) + { + mTurnAnimationThreshold = 0.05f; + } + else if (movestate == CharState_None && isTurning() + && mTurnAnimationThreshold > 0) + { + movestate = mMovementState; + } } if(movestate != CharState_None && !isTurning()) @@ -2028,8 +2046,10 @@ void CharacterController::update(float duration) if (isTurning()) { + // Adjust animation speed from 1.0 to 1.5 multiplier + float turnSpeed = std::min(1.5f, std::abs(rot.z()) / duration / static_cast(osg::PI)); if (duration > 0) - mAnimation->adjustSpeedMult(mCurrentMovement, std::min(1.5f, std::abs(rot.z()) / duration / static_cast(osg::PI))); + mAnimation->adjustSpeedMult(mCurrentMovement, std::max(turnSpeed, 1.0f)); } else if (mMovementState != CharState_None && mAdjustMovementAnimSpeed) { From a0d0e5d2db5ef49d803213c37bd39d09be4ebd6d Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 8 Aug 2018 16:52:09 +0400 Subject: [PATCH 13/53] Give jumping animations higher priority than movement ones --- apps/openmw/mwmechanics/character.cpp | 8 +------- apps/openmw/mwmechanics/character.hpp | 4 +++- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 054f0b64d..c101c2a05 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1980,15 +1980,9 @@ void CharacterController::update(float duration) else if(rot.z() != 0.0f && !sneak && !(mPtr == getPlayer() && MWBase::Environment::get().getWorld()->isFirstPerson())) { if(rot.z() > 0.0f) - { movestate = inwater ? CharState_SwimTurnRight : CharState_TurnRight; - mAnimation->disable(mCurrentJump); - } else if(rot.z() < 0.0f) - { movestate = inwater ? CharState_SwimTurnLeft : CharState_TurnLeft; - mAnimation->disable(mCurrentJump); - } } } @@ -2000,7 +1994,7 @@ void CharacterController::update(float duration) bool animPlaying = mAnimation->getInfo(mCurrentMovement, &complete); if (movestate == CharState_None && isTurning()) { - if ((animPlaying && complete < threshold) || mJumpState != jumpstate) + if (animPlaying && complete < threshold) movestate = mMovementState; } } diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 84630a479..754f551f9 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -31,8 +31,10 @@ enum Priority { Priority_WeaponLowerBody, Priority_SneakIdleLowerBody, Priority_SwimIdle, - Priority_Jump, Priority_Movement, + // Note: in vanilla movement anims have higher priority than jump ones. + // It causes issues with landing animations during movement. + Priority_Jump, Priority_Hit, Priority_Weapon, Priority_Block, From 71bcc11ba5b73b0bdc6d2d4aef055b544c8740f3 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 17 Jul 2018 20:59:05 +0400 Subject: [PATCH 14/53] Apply only crossbow reload animation to upper body --- apps/openmw/mwmechanics/character.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index f5d6f8584..b63704e76 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1215,6 +1215,7 @@ bool CharacterController::updateWeaponState() MWRender::Animation::AnimPriority priorityWeapon(Priority_Weapon); priorityWeapon[MWRender::Animation::BoneGroup_LowerBody] = Priority_WeaponLowerBody; + bool forcestateupdate = false; // We should not play equipping animation and sound during weapon->weapon transition @@ -1663,18 +1664,22 @@ bool CharacterController::updateWeaponState() break; } - // Note: apply reload animations only for upper body since blending with movement animations can give weird result. - // Especially noticable with crossbow reload animation. + // Note: apply crossbow reload animation only for upper body + // since blending with movement animations can give weird result. if(!start.empty()) { + int mask = MWRender::Animation::BlendMask_All; + if (mWeaponType == WeapType_Crossbow) + mask = MWRender::Animation::BlendMask_UpperBody; + mAnimation->disable(mCurrentWeapon); if (mUpperBodyState == UpperCharState_FollowStartToFollowStop) mAnimation->play(mCurrentWeapon, priorityWeapon, - MWRender::Animation::BlendMask_UpperBody, true, + mask, true, weapSpeed, start, stop, 0.0f, 0); else mAnimation->play(mCurrentWeapon, priorityWeapon, - MWRender::Animation::BlendMask_UpperBody, false, + mask, false, weapSpeed, start, stop, 0.0f, 0); } } From df577babe9d794e510af2425d52faa65a0cc6747 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 17 Jul 2018 21:00:13 +0400 Subject: [PATCH 15/53] Increase priority of 1st-person weapon animations to avoid issues with animation blending --- CHANGELOG.md | 1 + apps/openmw/mwmechanics/character.cpp | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a6621bf2..a2014e6be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -83,6 +83,7 @@ Bug #4563: Fast travel price logic checks destination cell instead of service actor cell Bug #4565: Underwater view distance should be limited Bug #4573: Player uses headtracking in the 1st-person mode + Bug #4575: Weird result of attack animation blending with movement animations Feature #2606: Editor: Implemented (optional) case sensitive global search Feature #3083: Play animation when NPC is casting spell via script Feature #3103: Provide option for disposition to get increased by successful trade diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index b63704e76..11056c674 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1212,9 +1212,10 @@ bool CharacterController::updateWeaponState() mWeapon = weapon != inv.end() ? *weapon : MWWorld::Ptr(); } + // Apply 1st-person weapon animations only for upper body MWRender::Animation::AnimPriority priorityWeapon(Priority_Weapon); - priorityWeapon[MWRender::Animation::BoneGroup_LowerBody] = Priority_WeaponLowerBody; - + if (mPtr != MWMechanics::getPlayer() || !MWBase::Environment::get().getWorld()->isFirstPerson()) + priorityWeapon[MWRender::Animation::BoneGroup_LowerBody] = Priority_WeaponLowerBody; bool forcestateupdate = false; From 780648b584df25c3becbe81dd6166479b3099c67 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 25 Jul 2018 18:50:45 +0400 Subject: [PATCH 16/53] Do not reset idle animations if we do not have ammo --- CHANGELOG.md | 1 + apps/openmw/mwmechanics/character.cpp | 22 ++++++++++++++-------- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a2014e6be..1e516eaf3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -84,6 +84,7 @@ Bug #4565: Underwater view distance should be limited Bug #4573: Player uses headtracking in the 1st-person mode Bug #4575: Weird result of attack animation blending with movement animations + Bug #4576: Reset of idle animations when attack can not be started Feature #2606: Editor: Implemented (optional) case sensitive global search Feature #3083: Play animation when NPC is casting spell via script Feature #3103: Provide option for disposition to get increased by successful trade diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 11056c674..9a6026cc3 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1360,14 +1360,7 @@ bool CharacterController::updateWeaponState() { MWWorld::Ptr player = getPlayer(); - // We should reset player's idle animation in the first-person mode. - if (mPtr == player && MWBase::Environment::get().getWorld()->isFirstPerson()) - mIdleState = CharState_None; - - // In other cases we should not break swim and sneak animations - if (mIdleState != CharState_IdleSneak && mIdleState != CharState_IdleSwim) - mIdleState = CharState_None; - + bool resetIdle = ammunition; if(mUpperBodyState == UpperCharState_WeapEquiped && (mHitState == CharState_None || mHitState == CharState_Block)) { MWBase::Environment::get().getWorld()->breakInvisibility(mPtr); @@ -1432,6 +1425,11 @@ bool CharacterController::updateWeaponState() 0.0f, 0); mUpperBodyState = UpperCharState_CastingSpell; } + else + { + resetIdle = false; + } + if (mPtr.getClass().hasInventoryStore(mPtr)) { MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr); @@ -1502,6 +1500,14 @@ bool CharacterController::updateWeaponState() } } + // We should reset player's idle animation in the first-person mode. + if (resetIdle && mPtr == player && MWBase::Environment::get().getWorld()->isFirstPerson()) + mIdleState = CharState_None; + + // In other cases we should not break swim and sneak animations + if (resetIdle && mIdleState != CharState_IdleSneak && mIdleState != CharState_IdleSwim) + mIdleState = CharState_None; + animPlaying = mAnimation->getInfo(mCurrentWeapon, &complete); if(mUpperBodyState == UpperCharState_MinAttackToMaxAttack && !isKnockedDown()) mAttackStrength = complete; From cde95979d0e8a5ea05a6e433763936aaa86cf17f Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Fri, 10 Aug 2018 09:29:01 +0400 Subject: [PATCH 17/53] Fix combat engagement for creatures --- apps/openmw/mwmechanics/actors.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index e3cd51799..2d535f57b 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -1346,7 +1346,7 @@ namespace MWMechanics { if (it->first == iter->first || isPlayer) // player is not AI-controlled continue; - engageCombat(iter->first, it->first, cachedAllies, isPlayer); + engageCombat(iter->first, it->first, cachedAllies, it->first == player); } } if (timerUpdateHeadTrack == 0) From ec9a1b0d0526c9aba7d7cdd0e68789ab0e747d50 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Mon, 30 Jul 2018 08:24:30 +0400 Subject: [PATCH 18/53] Handle RootCollisionNode, attached to non-root node (bug #4311) --- CHANGELOG.md | 1 + components/nifbullet/bulletnifloader.cpp | 42 ++++++++++++++++-------- components/nifbullet/bulletnifloader.hpp | 4 +-- 3 files changed, 31 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d5b62bf1a..1d6a20cac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,7 @@ Bug #4291: Non-persistent actors that started the game as dead do not play death animations Bug #4293: Faction members are not aware of faction ownerships in barter Bug #4307: World cleanup should remove dead bodies only if death animation is finished + Bug #4311: OpenMW does not handle RootCollisionNode correctly Bug #4327: Missing animations during spell/weapon stance switching Bug #4358: Running animation is interrupted when magic mode is toggled Bug #4368: Settings window ok button doesn't have key focus by default diff --git a/components/nifbullet/bulletnifloader.cpp b/components/nifbullet/bulletnifloader.cpp index be5a7d9d6..6f8c8f2c0 100644 --- a/components/nifbullet/bulletnifloader.cpp +++ b/components/nifbullet/bulletnifloader.cpp @@ -94,13 +94,16 @@ osg::ref_ptr BulletNifLoader::load(const Nif::NIFFilePtr& } else { - bool autogenerated = hasAutoGeneratedCollision(node); - // files with the name convention xmodel.nif usually have keyframes stored in a separate file xmodel.kf (see Animation::addAnimSource). // assume all nodes in the file will be animated const bool isAnimated = pathFileNameStartsWithX(nif->getFilename()); - handleNode(node, 0, autogenerated, isAnimated, autogenerated); + // If the mesh has RootCollisionNode, attached to actual root node, use it as collision mesh + const Nif::Node* rootCollisionNode = getCollisionNode(node); + if (rootCollisionNode) + handleNode(nif->getFilename(), rootCollisionNode, 0, false, isAnimated, false); + else + handleNode(nif->getFilename(), node, 0, true, isAnimated, true); if (mCompoundShape) { @@ -153,25 +156,34 @@ bool BulletNifLoader::findBoundingBox(const Nif::Node* node, int flags) return false; } -bool BulletNifLoader::hasAutoGeneratedCollision(const Nif::Node* rootNode) +const Nif::Node* BulletNifLoader::getCollisionNode(const Nif::Node* rootNode) { const Nif::NiNode *ninode = dynamic_cast(rootNode); if(ninode) { + // If root NiNode has only other NiNode as child, consider it as a wrapper, not as actual root node const Nif::NodeList &list = ninode->children; - for(size_t i = 0;i < list.length();i++) + if (list.length() == 1 && + rootNode->recType == Nif::RC_NiNode && + list[0].getPtr()->recType == Nif::RC_NiNode) { - if(!list[i].empty()) - { - if(list[i].getPtr()->recType == Nif::RC_RootCollisionNode) - return false; - } + return getCollisionNode(list[0].getPtr()); + } + + for(size_t i = 0; i < list.length(); i++) + { + if(list[i].empty()) + continue; + + const Nif::Node* childNode = list[i].getPtr(); + if(childNode->recType == Nif::RC_RootCollisionNode) + return childNode; } } - return true; + return nullptr; } -void BulletNifLoader::handleNode(const Nif::Node *node, int flags, +void BulletNifLoader::handleNode(const std::string& fileName, const Nif::Node *node, int flags, bool isCollisionNode, bool isAnimated, bool autogenerated) { // Accumulate the flags from all the child nodes. This works for all @@ -184,6 +196,9 @@ void BulletNifLoader::handleNode(const Nif::Node *node, int flags, isCollisionNode = isCollisionNode || (node->recType == Nif::RC_RootCollisionNode); + if (node->recType == Nif::RC_RootCollisionNode && autogenerated) + std::cerr << "Found RootCollisionNode attached to non-root node in " << fileName << ". Treat it as a common NiTriShape." << std::endl; + // Don't collide with AvoidNode shapes if(node->recType == Nif::RC_AvoidNode) flags |= 0x800; @@ -212,7 +227,6 @@ void BulletNifLoader::handleNode(const Nif::Node *node, int flags, // Marker can still have collision if the model explicitely specifies it via a RootCollisionNode. return; } - } } @@ -235,7 +249,7 @@ void BulletNifLoader::handleNode(const Nif::Node *node, int flags, for(size_t i = 0;i < list.length();i++) { if(!list[i].empty()) - handleNode(list[i].getPtr(), flags, isCollisionNode, isAnimated, autogenerated); + handleNode(fileName, list[i].getPtr(), flags, isCollisionNode, isAnimated, autogenerated); } } } diff --git a/components/nifbullet/bulletnifloader.hpp b/components/nifbullet/bulletnifloader.hpp index fff51933f..8fd9cc2a1 100644 --- a/components/nifbullet/bulletnifloader.hpp +++ b/components/nifbullet/bulletnifloader.hpp @@ -55,9 +55,9 @@ public: private: bool findBoundingBox(const Nif::Node* node, int flags = 0); - void handleNode(Nif::Node const *node, int flags, bool isCollisionNode, bool isAnimated=false, bool autogenerated=false); + void handleNode(const std::string& fileName, Nif::Node const *node, int flags, bool isCollisionNode, bool isAnimated=false, bool autogenerated=false); - bool hasAutoGeneratedCollision(const Nif::Node *rootNode); + const Nif::Node* getCollisionNode(const Nif::Node* rootNode); void handleNiTriShape(const Nif::NiTriShape *shape, int flags, const osg::Matrixf& transform, bool isAnimated); From 3527f3800e73dad9b64698fbc70aa3d423df5083 Mon Sep 17 00:00:00 2001 From: Capostrophic <21265616+Capostrophic@users.noreply.github.com> Date: Fri, 10 Aug 2018 20:07:38 +0300 Subject: [PATCH 19/53] Use the correct spell projectile speed GMST in AI aiming --- CHANGELOG.md | 1 + apps/openmw/mwmechanics/aicombat.cpp | 25 ++++++++++++------------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 775f0545d..f74af2427 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ Bug #3876: Landscape texture painting is misaligned Bug #3897: Have Goodbye give all choices the effects of Goodbye Bug #3911: [macOS] Typing in the "Content List name" dialog box produces double characters + Bug #3948: AiCombat moving target aiming uses incorrect speed for magic projectiles Bug #3950: FLATTEN_STATIC_TRANSFORMS optimization breaks animated collision shapes Bug #3993: Terrain texture blending map is not upscaled Bug #3997: Almalexia doesn't pace diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index bc6d22326..cd9709885 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -600,27 +600,26 @@ osg::Vec3f AimDirToMovingTarget(const MWWorld::Ptr& actor, const MWWorld::Ptr& t float duration, int weapType, float strength) { float projSpeed; + const MWWorld::Store& gmst = MWBase::Environment::get().getWorld()->getStore().get(); // get projectile speed (depending on weapon type) if (weapType == ESM::Weapon::MarksmanThrown) { - static float fThrownWeaponMinSpeed = - MWBase::Environment::get().getWorld()->getStore().get().find("fThrownWeaponMinSpeed")->getFloat(); - static float fThrownWeaponMaxSpeed = - MWBase::Environment::get().getWorld()->getStore().get().find("fThrownWeaponMaxSpeed")->getFloat(); + static float fThrownWeaponMinSpeed = gmst.find("fThrownWeaponMinSpeed")->getFloat(); + static float fThrownWeaponMaxSpeed = gmst.find("fThrownWeaponMaxSpeed")->getFloat(); - projSpeed = - fThrownWeaponMinSpeed + (fThrownWeaponMaxSpeed - fThrownWeaponMinSpeed) * strength; + projSpeed = fThrownWeaponMinSpeed + (fThrownWeaponMaxSpeed - fThrownWeaponMinSpeed) * strength; } - else + else if (weapType != 0) { - static float fProjectileMinSpeed = - MWBase::Environment::get().getWorld()->getStore().get().find("fProjectileMinSpeed")->getFloat(); - static float fProjectileMaxSpeed = - MWBase::Environment::get().getWorld()->getStore().get().find("fProjectileMaxSpeed")->getFloat(); + static float fProjectileMinSpeed = gmst.find("fProjectileMinSpeed")->getFloat(); + static float fProjectileMaxSpeed = gmst.find("fProjectileMaxSpeed")->getFloat(); - projSpeed = - fProjectileMinSpeed + (fProjectileMaxSpeed - fProjectileMinSpeed) * strength; + projSpeed = fProjectileMinSpeed + (fProjectileMaxSpeed - fProjectileMinSpeed) * strength; + } + else // weapType is 0 ==> it's a target spell projectile + { + projSpeed = gmst.find("fTargetSpellMaxSpeed")->getFloat(); } // idea: perpendicular to dir to target speed components of target move vector and projectile vector should be the same From ef631a0961d45e8be83775a84db57337c8846f43 Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 8 Jul 2018 20:26:57 +0300 Subject: [PATCH 20/53] Add dependency for GMock --- .travis.yml | 2 - CI/before_install.linux.sh | 8 - CI/before_script.linux.sh | 17 +- CI/build_googletest.sh | 13 + apps/openmw_test_suite/CMakeLists.txt | 8 +- cmake/FindGMock.cmake | 515 ++++++++++++++++++++++++++ 6 files changed, 547 insertions(+), 16 deletions(-) create mode 100755 CI/build_googletest.sh create mode 100644 cmake/FindGMock.cmake diff --git a/.travis.yml b/.travis.yml index c6facef2b..781f498e2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,8 +24,6 @@ addons: packages: [ # Dev cmake, clang-3.6, libunshield-dev, libtinyxml-dev, - # Tests - libgtest-dev, google-mock, # Boost libboost-filesystem-dev, libboost-program-options-dev, libboost-system-dev, # FFmpeg diff --git a/CI/before_install.linux.sh b/CI/before_install.linux.sh index 2b0e73110..25d05e619 100755 --- a/CI/before_install.linux.sh +++ b/CI/before_install.linux.sh @@ -1,11 +1,3 @@ #!/bin/sh sudo ln -s /usr/bin/clang-3.6 /usr/local/bin/clang sudo ln -s /usr/bin/clang++-3.6 /usr/local/bin/clang++ - -# build libgtest & libgtest_main -sudo mkdir /usr/src/gtest/build -cd /usr/src/gtest/build -sudo cmake .. -DBUILD_SHARED_LIBS=1 -sudo make -j4 -sudo ln -s /usr/src/gtest/build/libgtest.so /usr/lib/libgtest.so -sudo ln -s /usr/src/gtest/build/libgtest_main.so /usr/lib/libgtest_main.so diff --git a/CI/before_script.linux.sh b/CI/before_script.linux.sh index 93be1cb48..dd879989a 100755 --- a/CI/before_script.linux.sh +++ b/CI/before_script.linux.sh @@ -1,8 +1,21 @@ -#!/bin/sh +#!/bin/sh -e free -m + +env GENERATOR='Unix Makefiles' CONFIGURATION=Release CI/build_googletest.sh +GOOGLETEST_DIR="$(pwd)/googletest/build" + mkdir build cd build export CODE_COVERAGE=1 if [ "${CC}" = "clang" ]; then export CODE_COVERAGE=0; fi -${ANALYZE}cmake .. -DBUILD_WITH_CODE_COVERAGE=${CODE_COVERAGE} -DBUILD_UNITTESTS=1 -DCMAKE_INSTALL_PREFIX=/usr -DBINDIR=/usr/games -DCMAKE_BUILD_TYPE="None" -DUSE_SYSTEM_TINYXML=TRUE +${ANALYZE}cmake \ + -DBUILD_WITH_CODE_COVERAGE=${CODE_COVERAGE} \ + -DBUILD_UNITTESTS=1 \ + -DCMAKE_INSTALL_PREFIX=/usr \ + -DBINDIR=/usr/games \ + -DCMAKE_BUILD_TYPE="None" \ + -DUSE_SYSTEM_TINYXML=TRUE \ + -DGTEST_ROOT="${GOOGLETEST_DIR}" \ + -DGMOCK_ROOT="${GOOGLETEST_DIR}" \ + .. diff --git a/CI/build_googletest.sh b/CI/build_googletest.sh new file mode 100755 index 000000000..cd61379b5 --- /dev/null +++ b/CI/build_googletest.sh @@ -0,0 +1,13 @@ +#!/bin/sh -e + +git clone https://github.com/google/googletest.git +cd googletest +mkdir build +cd build +cmake \ + -D CMAKE_BUILD_TYPE="${CONFIGURATION}" \ + -D CMAKE_INSTALL_PREFIX=. \ + -G "${GENERATOR}" \ + .. +cmake --build . --config "${CONFIGURATION}" +cmake --build . --target install --config "${CONFIGURATION}" diff --git a/apps/openmw_test_suite/CMakeLists.txt b/apps/openmw_test_suite/CMakeLists.txt index 48c8be4d8..675da933d 100644 --- a/apps/openmw_test_suite/CMakeLists.txt +++ b/apps/openmw_test_suite/CMakeLists.txt @@ -1,7 +1,9 @@ find_package(GTest REQUIRED) +find_package(GMock REQUIRED) -if (GTEST_FOUND) +if (GTEST_FOUND AND GMOCK_FOUND) include_directories(SYSTEM ${GTEST_INCLUDE_DIRS}) + include_directories(SYSTEM ${GMOCK_INCLUDE_DIRS}) file(GLOB UNITTEST_SRC_FILES ../openmw/mwworld/store.cpp @@ -19,11 +21,9 @@ if (GTEST_FOUND) openmw_add_executable(openmw_test_suite openmw_test_suite.cpp ${UNITTEST_SRC_FILES}) - target_link_libraries(openmw_test_suite ${GTEST_BOTH_LIBRARIES} components) + target_link_libraries(openmw_test_suite ${GMOCK_LIBRARIES} components) # Fix for not visible pthreads functions for linker with glibc 2.15 if (UNIX AND NOT APPLE) target_link_libraries(openmw_test_suite ${CMAKE_THREAD_LIBS_INIT}) endif() endif() - - diff --git a/cmake/FindGMock.cmake b/cmake/FindGMock.cmake new file mode 100644 index 000000000..8d7324242 --- /dev/null +++ b/cmake/FindGMock.cmake @@ -0,0 +1,515 @@ +# Get the Google C++ Mocking Framework. +# (This file is almost an copy of the original FindGTest.cmake file, +# altered to download and compile GMock and GTest if not found +# in GMOCK_ROOT or GTEST_ROOT respectively, +# feel free to use it as it is or modify it for your own needs.) +# +# Defines the following variables: +# +# GMOCK_FOUND - Found or got the Google Mocking framework +# GTEST_FOUND - Found or got the Google Testing framework +# GMOCK_INCLUDE_DIRS - GMock include directory +# GTEST_INCLUDE_DIRS - GTest include direcotry +# +# Also defines the library variables below as normal variables +# +# GMOCK_BOTH_LIBRARIES - Both libgmock & libgmock_main +# GMOCK_LIBRARIES - libgmock +# GMOCK_MAIN_LIBRARIES - libgmock-main +# +# GTEST_BOTH_LIBRARIES - Both libgtest & libgtest_main +# GTEST_LIBRARIES - libgtest +# GTEST_MAIN_LIBRARIES - libgtest_main +# +# Accepts the following variables as input: +# +# GMOCK_ROOT - The root directory of the gmock install prefix +# GTEST_ROOT - The root directory of the gtest install prefix +# GMOCK_SRC_DIR -The directory of the gmock sources +# GMOCK_VER - The version of the gmock sources to be downloaded +# +#----------------------- +# Example Usage: +# +# set(GMOCK_ROOT "~/gmock") +# find_package(GMock REQUIRED) +# include_directories(${GMOCK_INCLUDE_DIRS}) +# +# add_executable(foo foo.cc) +# target_link_libraries(foo ${GMOCK_BOTH_LIBRARIES}) +# +#============================================================================= +# Copyright (c) 2016 Michel Estermann +# Copyright (c) 2016 Kamil Strzempowicz +# Copyright (c) 2011 Matej Svec +# +# CMake - Cross Platform Makefile Generator +# Copyright 2000-2016 Kitware, Inc. +# Copyright 2000-2011 Insight Software Consortium +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# * Neither the names of Kitware, Inc., the Insight Software Consortium, +# nor the names of their contributors may be used to endorse or promote +# products derived from this software without specific prior written +# permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# ------------------------------------------------------------------------------ +# +# The above copyright and license notice applies to distributions of +# CMake in source and binary form. Some source files contain additional +# notices of original copyright by their contributors; see each source +# for details. Third-party software packages supplied with CMake under +# compatible licenses provide their own copyright notices documented in +# corresponding subdirectories. +# +# ------------------------------------------------------------------------------ +# +# CMake was initially developed by Kitware with the following sponsorship: +# +# * National Library of Medicine at the National Institutes of Health +# as part of the Insight Segmentation and Registration Toolkit (ITK). +# +# * US National Labs (Los Alamos, Livermore, Sandia) ASC Parallel +# Visualization Initiative. +# +# * National Alliance for Medical Image Computing (NAMIC) is funded by the +# National Institutes of Health through the NIH Roadmap for Medical Research, +# Grant U54 EB005149. +# +# * Kitware, Inc. +#============================================================================= +# Thanks to Daniel Blezek for the GTEST_ADD_TESTS code + +function(gtest_add_tests executable extra_args) + if(NOT ARGN) + message(FATAL_ERROR "Missing ARGN: Read the documentation for GTEST_ADD_TESTS") + endif() + if(ARGN STREQUAL "AUTO") + # obtain sources used for building that executable + get_property(ARGN TARGET ${executable} PROPERTY SOURCES) + endif() + set(gtest_case_name_regex ".*\\( *([A-Za-z_0-9]+) *, *([A-Za-z_0-9]+) *\\).*") + set(gtest_test_type_regex "(TYPED_TEST|TEST_?[FP]?)") + foreach(source ${ARGN}) + file(READ "${source}" contents) + string(REGEX MATCHALL "${gtest_test_type_regex} *\\(([A-Za-z_0-9 ,]+)\\)" found_tests ${contents}) + foreach(hit ${found_tests}) + string(REGEX MATCH "${gtest_test_type_regex}" test_type ${hit}) + + # Parameterized tests have a different signature for the filter + if("x${test_type}" STREQUAL "xTEST_P") + string(REGEX REPLACE ${gtest_case_name_regex} "*/\\1.\\2/*" test_name ${hit}) + elseif("x${test_type}" STREQUAL "xTEST_F" OR "x${test_type}" STREQUAL "xTEST") + string(REGEX REPLACE ${gtest_case_name_regex} "\\1.\\2" test_name ${hit}) + elseif("x${test_type}" STREQUAL "xTYPED_TEST") + string(REGEX REPLACE ${gtest_case_name_regex} "\\1/*.\\2" test_name ${hit}) + else() + message(WARNING "Could not parse GTest ${hit} for adding to CTest.") + continue() + endif() + add_test(NAME ${test_name} COMMAND ${executable} --gtest_filter=${test_name} ${extra_args}) + endforeach() + endforeach() +endfunction() + +function(_append_debugs _endvar _library) + if(${_library} AND ${_library}_DEBUG) + set(_output optimized ${${_library}} debug ${${_library}_DEBUG}) + else() + set(_output ${${_library}}) + endif() + set(${_endvar} ${_output} PARENT_SCOPE) +endfunction() + +function(_gmock_find_library _name) + find_library(${_name} + NAMES ${ARGN} + HINTS + ENV GMOCK_ROOT + ${GMOCK_ROOT} + PATH_SUFFIXES ${_gmock_libpath_suffixes} + ) + mark_as_advanced(${_name}) +endfunction() + +function(_gtest_find_library _name) + find_library(${_name} + NAMES ${ARGN} + HINTS + ENV GTEST_ROOT + ${GTEST_ROOT} + PATH_SUFFIXES ${_gtest_libpath_suffixes} + ) + mark_as_advanced(${_name}) +endfunction() + +if(NOT DEFINED GMOCK_MSVC_SEARCH) + set(GMOCK_MSVC_SEARCH MD) +endif() + +set(_gmock_libpath_suffixes lib) +set(_gtest_libpath_suffixes lib) +if(MSVC) + if(GMOCK_MSVC_SEARCH STREQUAL "MD") + list(APPEND _gmock_libpath_suffixes + msvc/gmock-md/Debug + msvc/gmock-md/Release) + list(APPEND _gtest_libpath_suffixes + msvc/gtest-md/Debug + msvc/gtest-md/Release) + elseif(GMOCK_MSVC_SEARCH STREQUAL "MT") + list(APPEND _gmock_libpath_suffixes + msvc/gmock/Debug + msvc/gmock/Release) + list(APPEND _gtest_libpath_suffixes + msvc/gtest/Debug + msvc/gtest/Release) + endif() +endif() + +find_path(GMOCK_INCLUDE_DIR gmock/gmock.h + HINTS + $ENV{GMOCK_ROOT}/include + ${GMOCK_ROOT}/include + ) +mark_as_advanced(GMOCK_INCLUDE_DIR) + +find_path(GTEST_INCLUDE_DIR gtest/gtest.h + HINTS + $ENV{GTEST_ROOT}/include + ${GTEST_ROOT}/include + ) +mark_as_advanced(GTEST_INCLUDE_DIR) + +if(MSVC AND GMOCK_MSVC_SEARCH STREQUAL "MD") + # The provided /MD project files for Google Mock add -md suffixes to the + # library names. + _gmock_find_library(GMOCK_LIBRARY gmock-md gmock) + _gmock_find_library(GMOCK_LIBRARY_DEBUG gmock-mdd gmockd) + _gmock_find_library(GMOCK_MAIN_LIBRARY gmock_main-md gmock_main) + _gmock_find_library(GMOCK_MAIN_LIBRARY_DEBUG gmock_main-mdd gmock_maind) + + _gtest_find_library(GTEST_LIBRARY gtest-md gtest) + _gtest_find_library(GTEST_LIBRARY_DEBUG gtest-mdd gtestd) + _gtest_find_library(GTEST_MAIN_LIBRARY gtest_main-md gtest_main) + _gtest_find_library(GTEST_MAIN_LIBRARY_DEBUG gtest_main-mdd gtest_maind) +else() + _gmock_find_library(GMOCK_LIBRARY gmock) + _gmock_find_library(GMOCK_LIBRARY_DEBUG gmockd) + _gmock_find_library(GMOCK_MAIN_LIBRARY gmock_main) + _gmock_find_library(GMOCK_MAIN_LIBRARY_DEBUG gmock_maind) + + _gtest_find_library(GTEST_LIBRARY gtest) + _gtest_find_library(GTEST_LIBRARY_DEBUG gtestd) + _gtest_find_library(GTEST_MAIN_LIBRARY gtest_main) + _gtest_find_library(GTEST_MAIN_LIBRARY_DEBUG gtest_maind) +endif() + +if(NOT TARGET GTest::GTest) + add_library(GTest::GTest UNKNOWN IMPORTED) +endif() +if(NOT TARGET GTest::Main) + add_library(GTest::Main UNKNOWN IMPORTED) +endif() + +if(NOT TARGET GMock::GMock) + add_library(GMock::GMock UNKNOWN IMPORTED) +endif() + +if(NOT TARGET GMock::Main) + add_library(GMock::Main UNKNOWN IMPORTED) +endif() + +set(GMOCK_LIBRARY_EXISTS OFF) +set(GTEST_LIBRARY_EXISTS OFF) + +if(EXISTS "${GMOCK_LIBRARY}" OR EXISTS "${GMOCK_LIBRARY_DEBUG}" AND GMOCK_INCLUDE_DIR) + set(GMOCK_LIBRARY_EXISTS ON) +endif() + +if(EXISTS "${GTEST_LIBRARY}" OR EXISTS "${GTEST_LIBRARY_DEBUG}" AND GTEST_INCLUDE_DIR) + set(GTEST_LIBRARY_EXISTS ON) +endif() + +if(NOT (${GMOCK_LIBRARY_EXISTS} AND ${GTEST_LIBRARY_EXISTS})) + + include(ExternalProject) + + if(GTEST_USE_STATIC_LIBS) + set(GTEST_CMAKE_ARGS -Dgtest_force_shared_crt:BOOL=ON -DBUILD_SHARED_LIBS=OFF) + if(BUILD_SHARED_LIBS) + list(APPEND GTEST_CMAKE_ARGS + -DCMAKE_POSITION_INDEPENDENT_CODE=ON + -Dgtest_hide_internal_symbols=ON + -DCMAKE_CXX_VISIBILITY_PRESET=hidden + -DCMAKE_VISIBILITY_INLINES_HIDDEN=ON + -DCMAKE_POLICY_DEFAULT_CMP0063=NEW + ) + endif() + set(GTEST_LIBRARY_PREFIX ${CMAKE_STATIC_LIBRARY_PREFIX}) + else() + set(GTEST_CMAKE_ARGS -DBUILD_SHARED_LIBS=ON) + set(GTEST_LIBRARY_PREFIX ${CMAKE_SHARED_LIBRARY_PREFIX}) + endif() + if(WIN32) + list(APPEND GTEST_CMAKE_ARGS -Dgtest_disable_pthreads=ON) + endif() + + if("${GMOCK_SRC_DIR}" STREQUAL "") + message(STATUS "Downloading GMock / GTest version ${GMOCK_VER} from git") + if("${GMOCK_VER}" STREQUAL "1.6.0" OR "${GMOCK_VER}" STREQUAL "1.7.0") + set(GTEST_BIN_DIR "${GMOCK_ROOT}/src/gtest-build") + set(GTEST_LIBRARY "${GTEST_BIN_DIR}/${CMAKE_CFG_INTDIR}/${GTEST_LIBRARY_PREFIX}gtest${CMAKE_STATIC_LIBRARY_SUFFIX}") + set(GTEST_MAIN_LIBRARY "${GTEST_BIN_DIR}/${CMAKE_CFG_INTDIR}/${GTEST_LIBRARY_PREFIX}gtest_main${CMAKE_STATIC_LIBRARY_SUFFIX}") + mark_as_advanced(GTEST_LIBRARY) + mark_as_advanced(GTEST_MAIN_LIBRARY) + + externalproject_add( + gtest + GIT_REPOSITORY "https://github.com/google/googletest.git" + GIT_TAG "release-${GMOCK_VER}" + PREFIX ${GMOCK_ROOT} + INSTALL_COMMAND "" + LOG_DOWNLOAD ON + LOG_CONFIGURE ON + LOG_BUILD ON + CMAKE_ARGS + ${GTEST_CMAKE_ARGS} + BINARY_DIR ${GTEST_BIN_DIR} + BUILD_BYPRODUCTS + "${GTEST_LIBRARY}" + "${GTEST_MAIN_LIBRARY}" + ) + + set(GMOCK_BIN_DIR "${GMOCK_ROOT}/src/gmock-build") + set(GMOCK_LIBRARY "${GMOCK_BIN_DIR}/${CMAKE_CFG_INTDIR}/${GTEST_LIBRARY_PREFIX}gmock${CMAKE_STATIC_LIBRARY_SUFFIX}") + set(GMOCK_MAIN_LIBRARY "${GMOCK_BIN_DIR}/${CMAKE_CFG_INTDIR}/${GTEST_LIBRARY_PREFIX}gmock_main${CMAKE_STATIC_LIBRARY_SUFFIX}") + mark_as_advanced(GMOCK_LIBRARY) + mark_as_advanced(GMOCK_MAIN_LIBRARY) + + externalproject_add( + gmock + GIT_REPOSITORY "https://github.com/google/googlemock.git" + GIT_TAG "release-${GMOCK_VER}" + PREFIX ${GMOCK_ROOT} + INSTALL_COMMAND "" + LOG_DOWNLOAD ON + LOG_CONFIGURE ON + LOG_BUILD ON + CMAKE_ARGS + ${GTEST_CMAKE_ARGS} + BINARY_DIR ${GMOCK_BIN_DIR} + BUILD_BYPRODUCTS + "${GMOCK_LIBRARY}" + "${GMOCK_MAIN_LIBRARY}" + ) + + add_dependencies(gmock gtest) + + add_dependencies(GTest::GTest gtest) + add_dependencies(GTest::Main gtest) + add_dependencies(GMock::GMock gmock) + add_dependencies(GMock::Main gmock) + + externalproject_get_property(gtest source_dir) + set(GTEST_INCLUDE_DIR "${source_dir}/include") + mark_as_advanced(GTEST_INCLUDE_DIR) + externalproject_get_property(gmock source_dir) + set(GMOCK_INCLUDE_DIR "${source_dir}/include") + mark_as_advanced(GMOCK_INCLUDE_DIR) + else() #1.8.0 + set(GMOCK_BIN_DIR "${GMOCK_ROOT}/src/gmock-build") + set(GTEST_LIBRARY "${GMOCK_BIN_DIR}/googlemock/gtest/${CMAKE_CFG_INTDIR}/${GTEST_LIBRARY_PREFIX}gtest${CMAKE_STATIC_LIBRARY_SUFFIX}") + set(GTEST_MAIN_LIBRARY "${GMOCK_BIN_DIR}/googlemock/gtest/${CMAKE_CFG_INTDIR}/${GTEST_LIBRARY_PREFIX}gtest_main${CMAKE_STATIC_LIBRARY_SUFFIX}") + set(GMOCK_LIBRARY "${GMOCK_BIN_DIR}/googlemock/${CMAKE_CFG_INTDIR}/${GTEST_LIBRARY_PREFIX}gmock${CMAKE_STATIC_LIBRARY_SUFFIX}") + set(GMOCK_MAIN_LIBRARY "${GMOCK_BIN_DIR}/googlemock/${CMAKE_CFG_INTDIR}/${GTEST_LIBRARY_PREFIX}gmock_main${CMAKE_STATIC_LIBRARY_SUFFIX}") + mark_as_advanced(GTEST_LIBRARY) + mark_as_advanced(GTEST_MAIN_LIBRARY) + mark_as_advanced(GMOCK_LIBRARY) + mark_as_advanced(GMOCK_MAIN_LIBRARY) + + externalproject_add( + gmock + GIT_REPOSITORY "https://github.com/google/googletest.git" + GIT_TAG "release-${GMOCK_VER}" + PREFIX ${GMOCK_ROOT} + INSTALL_COMMAND "" + LOG_DOWNLOAD ON + LOG_CONFIGURE ON + LOG_BUILD ON + CMAKE_ARGS + ${GTEST_CMAKE_ARGS} + BINARY_DIR "${GMOCK_BIN_DIR}" + BUILD_BYPRODUCTS + "${GTEST_LIBRARY}" + "${GTEST_MAIN_LIBRARY}" + "${GMOCK_LIBRARY}" + "${GMOCK_MAIN_LIBRARY}" + ) + + add_dependencies(GTest::GTest gmock) + add_dependencies(GTest::Main gmock) + add_dependencies(GMock::GMock gmock) + add_dependencies(GMock::Main gmock) + + externalproject_get_property(gmock source_dir) + set(GTEST_INCLUDE_DIR "${source_dir}/googletest/include") + set(GMOCK_INCLUDE_DIR "${source_dir}/googlemock/include") + mark_as_advanced(GMOCK_INCLUDE_DIR) + mark_as_advanced(GTEST_INCLUDE_DIR) + endif() + + # Prevent CMake from complaining about these directories missing when the libgtest/libgmock targets get used as dependencies + file(MAKE_DIRECTORY ${GTEST_INCLUDE_DIR} ${GMOCK_INCLUDE_DIR}) + else() + message(STATUS "Building Gmock / Gtest from dir ${GMOCK_SRC_DIR}") + + set(GMOCK_BIN_DIR "${GMOCK_ROOT}/src/gmock-build") + set(GTEST_LIBRARY "${GMOCK_BIN_DIR}/gtest/${CMAKE_CFG_INTDIR}/${GTEST_LIBRARY_PREFIX}gtest${CMAKE_STATIC_LIBRARY_SUFFIX}") + set(GTEST_MAIN_LIBRARY "${GMOCK_BIN_DIR}/gtest/${CMAKE_CFG_INTDIR}/${GTEST_LIBRARY_PREFIX}gtest_main${CMAKE_STATIC_LIBRARY_SUFFIX}") + set(GMOCK_LIBRARY "${GMOCK_BIN_DIR}/${CMAKE_CFG_INTDIR}/${GTEST_LIBRARY_PREFIX}gmock${CMAKE_STATIC_LIBRARY_SUFFIX}") + set(GMOCK_MAIN_LIBRARY "${GMOCK_BIN_DIR}/${CMAKE_CFG_INTDIR}/${GTEST_LIBRARY_PREFIX}gmock_main${CMAKE_STATIC_LIBRARY_SUFFIX}") + mark_as_advanced(GTEST_LIBRARY) + mark_as_advanced(GTEST_MAIN_LIBRARY) + mark_as_advanced(GMOCK_LIBRARY) + mark_as_advanced(GMOCK_MAIN_LIBRARY) + + if(EXISTS "${GMOCK_SRC_DIR}/gtest/include/gtest/gtest.h") + set(GTEST_INCLUDE_DIR "${GMOCK_SRC_DIR}/gtest/include") + mark_as_advanced(GTEST_INCLUDE_DIR) + endif() + if(EXISTS "${GMOCK_SRC_DIR}/include/gmock/gmock.h") + set(GMOCK_INCLUDE_DIR "${GMOCK_SRC_DIR}/include") + mark_as_advanced(GMOCK_INCLUDE_DIR) + elseif(EXISTS "${GMOCK_SRC_DIR}/../../include/gmock/gmock.h") + set(GMOCK_INCLUDE_DIR "${GMOCK_SRC_DIR}/../../include") + if(IS_ABSOLUTE "${GMOCK_INCLUDE_DIR}") + get_filename_component(GMOCK_INCLUDE_DIR "${GMOCK_INCLUDE_DIR}" ABSOLUTE) + endif() + mark_as_advanced(GMOCK_INCLUDE_DIR) + endif() + + externalproject_add( + gmock + SOURCE_DIR ${GMOCK_SRC_DIR} + PREFIX ${GMOCK_ROOT} + INSTALL_COMMAND "" + LOG_DOWNLOAD ON + LOG_CONFIGURE ON + LOG_BUILD ON + CMAKE_ARGS + ${GTEST_CMAKE_ARGS} + BINARY_DIR "${GMOCK_BIN_DIR}" + BUILD_BYPRODUCTS + "${GTEST_LIBRARY}" + "${GTEST_MAIN_LIBRARY}" + "${GMOCK_LIBRARY}" + "${GMOCK_MAIN_LIBRARY}" + ) + + add_dependencies(GTest::GTest gmock) + add_dependencies(GTest::Main gmock) + add_dependencies(GMock::GMock gmock) + add_dependencies(GMock::Main gmock) + endif() +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(GTest DEFAULT_MSG GTEST_LIBRARY GTEST_INCLUDE_DIR GTEST_MAIN_LIBRARY) +find_package_handle_standard_args(GMock DEFAULT_MSG GMOCK_LIBRARY GMOCK_INCLUDE_DIR GMOCK_MAIN_LIBRARY) + +include(CMakeFindDependencyMacro) +find_dependency(Threads) + +set_target_properties(GTest::GTest PROPERTIES + INTERFACE_LINK_LIBRARIES "Threads::Threads" + IMPORTED_LINK_INTERFACE_LANGUAGES "CXX" + IMPORTED_LOCATION "${GTEST_LIBRARY}" + ) + +if(GTEST_INCLUDE_DIR) + set_target_properties(GTest::GTest PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${GTEST_INCLUDE_DIR}" + INTERFACE_SYSTEM_INCLUDE_DIRECTORIES "${GTEST_INCLUDE_DIR}" + ) +endif() + +set_target_properties(GTest::Main PROPERTIES + INTERFACE_LINK_LIBRARIES "GTest::GTest" + IMPORTED_LINK_INTERFACE_LANGUAGES "CXX" + IMPORTED_LOCATION "${GTEST_MAIN_LIBRARY}") + +set_target_properties(GMock::GMock PROPERTIES + INTERFACE_LINK_LIBRARIES "Threads::Threads" + IMPORTED_LINK_INTERFACE_LANGUAGES "CXX" + IMPORTED_LOCATION "${GMOCK_LIBRARY}") + +if(GMOCK_INCLUDE_DIR) + set_target_properties(GMock::GMock PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${GMOCK_INCLUDE_DIR}" + INTERFACE_SYSTEM_INCLUDE_DIRECTORIES "${GMOCK_INCLUDE_DIR}" + ) + if(GMOCK_VER VERSION_LESS "1.7") + # GMock 1.6 still has GTest as an external link-time dependency, + # so just specify it on the link interface. + set_property(TARGET GMock::GMock APPEND PROPERTY + INTERFACE_LINK_LIBRARIES GTest::GTest) + elseif(GTEST_INCLUDE_DIR) + # GMock 1.7 and beyond doesn't have it as a link-time dependency anymore, + # so merge it's compile-time interface (include dirs) with ours. + set_property(TARGET GMock::GMock APPEND PROPERTY + INTERFACE_INCLUDE_DIRECTORIES "${GTEST_INCLUDE_DIR}") + set_property(TARGET GMock::GMock APPEND PROPERTY + INTERFACE_SYSTEM_INCLUDE_DIRECTORIES "${GTEST_INCLUDE_DIR}") + endif() +endif() + +set_target_properties(GMock::Main PROPERTIES + INTERFACE_LINK_LIBRARIES "GMock::GMock" + IMPORTED_LINK_INTERFACE_LANGUAGES "CXX" + IMPORTED_LOCATION "${GMOCK_MAIN_LIBRARY}") + +if(GTEST_FOUND) + set(GTEST_INCLUDE_DIRS ${GTEST_INCLUDE_DIR}) + set(GTEST_LIBRARIES GTest::GTest) + set(GTEST_MAIN_LIBRARIES GTest::Main) + set(GTEST_BOTH_LIBRARIES ${GTEST_LIBRARIES} ${GTEST_MAIN_LIBRARIES}) + if(VERBOSE) + message(STATUS "GTest includes: ${GTEST_INCLUDE_DIRS}") + message(STATUS "GTest libs: ${GTEST_BOTH_LIBRARIES}") + endif() +endif() + +if(GMOCK_FOUND) + set(GMOCK_INCLUDE_DIRS ${GMOCK_INCLUDE_DIR}) + set(GMOCK_LIBRARIES GMock::GMock) + set(GMOCK_MAIN_LIBRARIES GMock::Main) + set(GMOCK_BOTH_LIBRARIES ${GMOCK_LIBRARIES} ${GMOCK_MAIN_LIBRARIES}) + if(VERBOSE) + message(STATUS "GMock includes: ${GMOCK_INCLUDE_DIRS}") + message(STATUS "GMock libs: ${GMOCK_BOTH_LIBRARIES}") + endif() +endif() From 6afc1dc3a05d43c5f56d4378f89eb7de4f545bb2 Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 8 Jul 2018 20:27:46 +0300 Subject: [PATCH 21/53] Support build with coverage for components and test suite --- apps/openmw_test_suite/CMakeLists.txt | 5 +++++ components/CMakeLists.txt | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/apps/openmw_test_suite/CMakeLists.txt b/apps/openmw_test_suite/CMakeLists.txt index 675da933d..eb3492ed5 100644 --- a/apps/openmw_test_suite/CMakeLists.txt +++ b/apps/openmw_test_suite/CMakeLists.txt @@ -26,4 +26,9 @@ if (GTEST_FOUND AND GMOCK_FOUND) if (UNIX AND NOT APPLE) target_link_libraries(openmw_test_suite ${CMAKE_THREAD_LIBS_INIT}) endif() + + if (BUILD_WITH_CODE_COVERAGE) + add_definitions(--coverage) + target_link_libraries(openmw_test_suite gcov) + endif() endif() diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index e2e6b97bb..a94119099 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -242,6 +242,11 @@ if (UNIX AND NOT APPLE) target_link_libraries(components ${CMAKE_THREAD_LIBS_INIT}) endif() +if (BUILD_WITH_CODE_COVERAGE) + add_definitions(--coverage) + target_link_libraries(components gcov) +endif() + # Make the variable accessible for other subdirectories set(COMPONENT_FILES ${COMPONENT_FILES} PARENT_SCOPE) From f2a63bcf3512236d04558ca999a400128d16e21d Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 8 Jul 2018 22:22:34 +0300 Subject: [PATCH 22/53] Add unit tests for BulletNifLoader --- apps/openmw_test_suite/CMakeLists.txt | 2 + .../nifloader/testbulletnifloader.cpp | 951 ++++++++++++++++++ .../bullethelpers/processtrianglecallback.hpp | 34 + components/nif/niffile.hpp | 43 +- components/nif/recordptr.hpp | 8 + components/nifbullet/bulletnifloader.cpp | 13 +- components/nifbullet/bulletnifloader.hpp | 2 +- components/resource/bulletshapemanager.cpp | 2 +- 8 files changed, 1037 insertions(+), 18 deletions(-) create mode 100644 apps/openmw_test_suite/nifloader/testbulletnifloader.cpp create mode 100644 components/bullethelpers/processtrianglecallback.hpp diff --git a/apps/openmw_test_suite/CMakeLists.txt b/apps/openmw_test_suite/CMakeLists.txt index eb3492ed5..bc39bc2be 100644 --- a/apps/openmw_test_suite/CMakeLists.txt +++ b/apps/openmw_test_suite/CMakeLists.txt @@ -15,6 +15,8 @@ if (GTEST_FOUND AND GMOCK_FOUND) esm/test_fixed_string.cpp misc/test_stringops.cpp + + nifloader/testbulletnifloader.cpp ) source_group(apps\\openmw_test_suite FILES openmw_test_suite.cpp ${UNITTEST_SRC_FILES}) diff --git a/apps/openmw_test_suite/nifloader/testbulletnifloader.cpp b/apps/openmw_test_suite/nifloader/testbulletnifloader.cpp new file mode 100644 index 000000000..a2311be49 --- /dev/null +++ b/apps/openmw_test_suite/nifloader/testbulletnifloader.cpp @@ -0,0 +1,951 @@ +#include +#include +#include + +#include +#include +#include + +#include +#include + +namespace +{ + template + bool compareObjects(const T* lhs, const T* rhs) + { + return (!lhs && !rhs) || (lhs && rhs && *lhs == *rhs); + } + + std::vector getTriangles(const btBvhTriangleMeshShape& shape) + { + std::vector result; + auto callback = BulletHelpers::makeProcessTriangleCallback([&] (btVector3* triangle, int, int) { + for (std::size_t i = 0; i < 3; ++i) + result.push_back(triangle[i]); + }); + btVector3 aabbMin; + btVector3 aabbMax; + shape.getAabb(btTransform::getIdentity(), aabbMin, aabbMax); + shape.processAllTriangles(&callback, aabbMin, aabbMax); + return result; + } +} + +static std::ostream& operator <<(std::ostream& stream, const btVector3& value) +{ + return stream << "btVector3 {" + << std::setprecision(std::numeric_limits::max_exponent10) << value.getX() << ", " + << std::setprecision(std::numeric_limits::max_exponent10) << value.getY() << ", " + << std::setprecision(std::numeric_limits::max_exponent10) << value.getZ() << "}"; +} + +static std::ostream& operator <<(std::ostream& stream, const btMatrix3x3& value) +{ + stream << "btMatrix3x3 {"; + for (int i = 0; i < 3; ++i) + stream << value.getRow(i) << ", "; + return stream << "}"; +} + +static std::ostream& operator <<(std::ostream& stream, const btTransform& value) +{ + return stream << "btTransform {" << value.getBasis() << ", " << value.getOrigin() << "}"; +} + +static std::ostream& operator <<(std::ostream& stream, const btCollisionShape* value); + +static std::ostream& operator <<(std::ostream& stream, const btCompoundShape& value) +{ + stream << "btCompoundShape {" << value.getLocalScaling() << ", "; + stream << "{"; + for (int i = 0; i < value.getNumChildShapes(); ++i) + stream << value.getChildShape(i) << ", "; + stream << "},"; + stream << "{"; + for (int i = 0; i < value.getNumChildShapes(); ++i) + stream << value.getChildTransform(i) << ", "; + stream << "}"; + return stream << "}"; +} + +static std::ostream& operator <<(std::ostream& stream, const btBoxShape& value) +{ + return stream << "btBoxShape {" << value.getLocalScaling() << ", " << value.getHalfExtentsWithoutMargin() << "}"; +} + +namespace Resource +{ + +static std::ostream& operator <<(std::ostream& stream, const TriangleMeshShape& value) +{ + stream << "Resource::TriangleMeshShape {" << value.getLocalScaling() << ", " + << value.usesQuantizedAabbCompression() << ", " << value.getOwnsBvh() << ", {"; + auto callback = BulletHelpers::makeProcessTriangleCallback([&] (btVector3* triangle, int, int) { + for (std::size_t i = 0; i < 3; ++i) + stream << triangle[i] << ", "; + }); + btVector3 aabbMin; + btVector3 aabbMax; + value.getAabb(btTransform::getIdentity(), aabbMin, aabbMax); + value.processAllTriangles(&callback, aabbMin, aabbMax); + return stream << "}}"; +} + +} + +static std::ostream& operator <<(std::ostream& stream, const btCollisionShape& value) +{ + switch (value.getShapeType()) + { + case COMPOUND_SHAPE_PROXYTYPE: + return stream << static_cast(value); + case BOX_SHAPE_PROXYTYPE: + return stream << static_cast(value); + case TRIANGLE_MESH_SHAPE_PROXYTYPE: + if (const auto casted = dynamic_cast(&value)) + return stream << *casted; + break; + } + return stream << "btCollisionShape {" << value.getShapeType() << "}"; +} + +static std::ostream& operator <<(std::ostream& stream, const btCollisionShape* value) +{ + return value ? stream << "&" << *value : stream << "nullptr"; +} + +namespace osg +{ + static std::ostream& operator <<(std::ostream& stream, const Vec3f& value) + { + return stream << "osg::Vec3f {" + << value.x() << ", " + << value.y() << ", " + << value.z() << "}"; + } +} + +namespace std +{ + static std::ostream& operator <<(std::ostream& stream, const map& value) + { + stream << "std::map {"; + for (const auto& v : value) + stream << "{" << v.first << ", " << v.second << "},"; + return stream << "}"; + } +} + +namespace Resource +{ + static bool operator ==(const Resource::BulletShape& lhs, const Resource::BulletShape& rhs) + { + return compareObjects(lhs.mCollisionShape, rhs.mCollisionShape) + && lhs.mCollisionBoxHalfExtents == rhs.mCollisionBoxHalfExtents + && lhs.mCollisionBoxTranslate == rhs.mCollisionBoxTranslate + && lhs.mAnimatedShapes == rhs.mAnimatedShapes; + } + + static std::ostream& operator <<(std::ostream& stream, const Resource::BulletShape& value) + { + return stream << "Resource::BulletShape {" + << value.mCollisionShape << ", " + << value.mCollisionBoxHalfExtents << ", " + << value.mAnimatedShapes + << "}"; + } +} + +static bool operator ==(const btCollisionShape& lhs, const btCollisionShape& rhs); + +static bool operator ==(const btCompoundShape& lhs, const btCompoundShape& rhs) +{ + if (lhs.getNumChildShapes() != rhs.getNumChildShapes() || lhs.getLocalScaling() != rhs.getLocalScaling()) + return false; + for (int i = 0; i < lhs.getNumChildShapes(); ++i) + { + if (!compareObjects(lhs.getChildShape(i), rhs.getChildShape(i)) + || !(lhs.getChildTransform(i) == rhs.getChildTransform(i))) + return false; + } + return true; +} + +static bool operator ==(const btBoxShape& lhs, const btBoxShape& rhs) +{ + return lhs.getLocalScaling() == rhs.getLocalScaling() + && lhs.getHalfExtentsWithoutMargin() == rhs.getHalfExtentsWithoutMargin(); +} + +static bool operator ==(const btBvhTriangleMeshShape& lhs, const btBvhTriangleMeshShape& rhs) +{ + return lhs.getLocalScaling() == rhs.getLocalScaling() + && lhs.usesQuantizedAabbCompression() == rhs.usesQuantizedAabbCompression() + && lhs.getOwnsBvh() == rhs.getOwnsBvh() + && getTriangles(lhs) == getTriangles(rhs); +} + +static bool operator ==(const btCollisionShape& lhs, const btCollisionShape& rhs) +{ + if (lhs.getShapeType() != rhs.getShapeType()) + return false; + switch (lhs.getShapeType()) + { + case COMPOUND_SHAPE_PROXYTYPE: + return static_cast(lhs) == static_cast(rhs); + case BOX_SHAPE_PROXYTYPE: + return static_cast(lhs) == static_cast(rhs); + case TRIANGLE_MESH_SHAPE_PROXYTYPE: + if (const auto lhsCasted = dynamic_cast(&lhs)) + if (const auto rhsCasted = dynamic_cast(&rhs)) + return *lhsCasted == *rhsCasted; + return false; + } + return false; +} + +namespace +{ + using namespace testing; + using NifBullet::BulletNifLoader; + + void init(Nif::Transformation& value) + { + value = Nif::Transformation::getIdentity(); + } + + void init(Nif::Extra& value) + { + value.extra = Nif::ExtraPtr(nullptr); + } + + void init(Nif::Controlled& value) + { + init(static_cast(value)); + value.controller = Nif::ControllerPtr(nullptr); + } + + void init(Nif::Named& value) + { + init(static_cast(value)); + } + + void init(Nif::Node& value) + { + init(static_cast(value)); + value.flags = 0; + init(value.trafo); + value.hasBounds = false; + value.parent = nullptr; + value.isBone = false; + } + + void init(Nif::NiTriShape& value) + { + init(static_cast(value)); + value.recType = Nif::RC_NiTriShape; + value.data = Nif::NiTriShapeDataPtr(nullptr); + value.skin = Nif::NiSkinInstancePtr(nullptr); + } + + void init(Nif::NiSkinInstance& value) + { + value.data = Nif::NiSkinDataPtr(nullptr); + value.root = Nif::NodePtr(nullptr); + } + + void init(Nif::Controller& value) + { + value.next = Nif::ControllerPtr(nullptr); + value.flags = 0; + value.frequency = 0; + value.phase = 0; + value.timeStart = 0; + value.timeStop = 0; + value.target = Nif::ControlledPtr(nullptr); + } + + void copy(const btTransform& src, Nif::Transformation& dst) { + dst.pos = osg::Vec3f(src.getOrigin().x(), src.getOrigin().y(), src.getOrigin().z()); + for (int row = 0; row < 3; ++row) + for (int column = 0; column < 3; ++column) + dst.rotation.mValues[column][row] = src.getBasis().getRow(row)[column]; + } + + struct NifFileMock : Nif::File + { + MOCK_CONST_METHOD1(fail, void (const std::string&)); + MOCK_CONST_METHOD1(warn, void (const std::string&)); + MOCK_CONST_METHOD1(getRecord, Nif::Record* (std::size_t)); + MOCK_CONST_METHOD0(numRecords, std::size_t ()); + MOCK_CONST_METHOD1(getRoot, Nif::Record* (std::size_t)); + MOCK_CONST_METHOD0(numRoots, std::size_t ()); + MOCK_METHOD1(setUseSkinning, void (bool)); + MOCK_CONST_METHOD0(getUseSkinning, bool ()); + MOCK_CONST_METHOD0(getFilename, std::string ()); + }; + + struct RecordMock : Nif::Record + { + MOCK_METHOD1(read, void (Nif::NIFStream *nif)); + }; + + struct TestBulletNifLoader : Test + { + BulletNifLoader mLoader; + const StrictMock mNifFile; + Nif::Node mNode; + Nif::Node mNode2; + Nif::NiNode mNiNode; + Nif::NiNode mNiNode2; + Nif::NiNode mNiNode3; + Nif::NiTriShapeData mNiTriShapeData; + Nif::NiTriShape mNiTriShape; + Nif::NiTriShapeData mNiTriShapeData2; + Nif::NiTriShape mNiTriShape2; + Nif::NiSkinInstance mNiSkinInstance; + Nif::NiStringExtraData mNiStringExtraData; + Nif::NiStringExtraData mNiStringExtraData2; + Nif::Controller mController; + btTransform mTransform {btMatrix3x3(btQuaternion(btVector3(1, 0, 0), 0.5f)), btVector3(1, 2, 3)}; + btTransform mResultTransform { + btMatrix3x3( + 1, 0, 0, + 0, 0.82417738437652587890625, 0.56633174419403076171875, + 0, -0.56633174419403076171875, 0.82417738437652587890625 + ), + btVector3(1, 2, 3) + }; + btTransform mResultTransform2 { + btMatrix3x3( + 1, 0, 0, + 0, 0.7951543331146240234375, 0.606407105922698974609375, + 0, -0.606407105922698974609375, 0.7951543331146240234375 + ), + btVector3(4, 8, 12) + }; + + TestBulletNifLoader() + { + init(mNode); + init(mNode2); + init(mNiNode); + init(mNiNode2); + init(mNiNode3); + init(mNiTriShape); + init(mNiTriShape2); + init(mNiSkinInstance); + init(mNiStringExtraData); + init(mNiStringExtraData2); + init(mController); + + mNiTriShapeData.vertices = {osg::Vec3f(0, 0, 0), osg::Vec3f(1, 0, 0), osg::Vec3f(1, 1, 0)}; + mNiTriShapeData.triangles = {0, 1, 2}; + mNiTriShape.data = Nif::NiTriShapeDataPtr(&mNiTriShapeData); + + mNiTriShapeData2.vertices = {osg::Vec3f(0, 0, 1), osg::Vec3f(1, 0, 1), osg::Vec3f(1, 1, 1)}; + mNiTriShapeData2.triangles = {0, 1, 2}; + mNiTriShape2.data = Nif::NiTriShapeDataPtr(&mNiTriShapeData2); + } + }; + + TEST_F(TestBulletNifLoader, for_zero_num_roots_should_return_default) + { + EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(0)); + const auto result = mLoader.load(mNifFile); + + Resource::BulletShape expected; + + EXPECT_EQ(*result, expected); + } + + TEST_F(TestBulletNifLoader, for_root_not_nif_node_should_return_default) + { + StrictMock record; + + EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); + EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&record)); + const auto result = mLoader.load(mNifFile); + + Resource::BulletShape expected; + + EXPECT_EQ(*result, expected); + } + + TEST_F(TestBulletNifLoader, for_default_root_nif_node_should_return_default) + { + EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); + EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNode)); + EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); + const auto result = mLoader.load(mNifFile); + + Resource::BulletShape expected; + + EXPECT_EQ(*result, expected); + } + + TEST_F(TestBulletNifLoader, for_default_root_collision_node_nif_node_should_return_default) + { + mNode.recType = Nif::RC_RootCollisionNode; + + EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); + EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNode)); + EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); + const auto result = mLoader.load(mNifFile); + + Resource::BulletShape expected; + + EXPECT_EQ(*result, expected); + } + + TEST_F(TestBulletNifLoader, for_default_root_nif_node_and_filename_starting_with_x_should_return_default) + { + EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); + EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNode)); + EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("xtest.nif")); + const auto result = mLoader.load(mNifFile); + + Resource::BulletShape expected; + + EXPECT_EQ(*result, expected); + } + + TEST_F(TestBulletNifLoader, for_root_nif_node_with_bounding_box_should_return_shape_with_compound_shape_and_box_inside) + { + mNode.hasBounds = true; + mNode.flags |= Nif::NiNode::Flag_BBoxCollision; + mNode.boundXYZ = osg::Vec3f(1, 2, 3); + mNode.boundPos = osg::Vec3f(-1, -2, -3); + + EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); + EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNode)); + const auto result = mLoader.load(mNifFile); + + Resource::BulletShape expected; + expected.mCollisionBoxHalfExtents = osg::Vec3f(1, 2, 3); + expected.mCollisionBoxTranslate = osg::Vec3f(-1, -2, -3); + std::unique_ptr box(new btBoxShape(btVector3(1, 2, 3))); + std::unique_ptr shape(new btCompoundShape); + shape->addChildShape(btTransform(btMatrix3x3::getIdentity(), btVector3(-1, -2, -3)), box.release()); + expected.mCollisionShape = shape.release(); + + EXPECT_EQ(*result, expected); + } + + TEST_F(TestBulletNifLoader, for_child_nif_node_with_bounding_box) + { + mNode.hasBounds = true; + mNode.flags |= Nif::NiNode::Flag_BBoxCollision; + mNode.boundXYZ = osg::Vec3f(1, 2, 3); + mNode.boundPos = osg::Vec3f(-1, -2, -3); + mNiNode.children = Nif::NodeList(std::vector({Nif::NodePtr(&mNode)})); + + EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); + EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); + const auto result = mLoader.load(mNifFile); + + Resource::BulletShape expected; + expected.mCollisionBoxHalfExtents = osg::Vec3f(1, 2, 3); + expected.mCollisionBoxTranslate = osg::Vec3f(-1, -2, -3); + std::unique_ptr box(new btBoxShape(btVector3(1, 2, 3))); + std::unique_ptr shape(new btCompoundShape); + shape->addChildShape(btTransform(btMatrix3x3::getIdentity(), btVector3(-1, -2, -3)), box.release()); + expected.mCollisionShape = shape.release(); + + EXPECT_EQ(*result, expected); + } + + TEST_F(TestBulletNifLoader, for_root_and_child_nif_node_with_bounding_box_but_root_without_flag_should_use_child_bounds) + { + mNode.hasBounds = true; + mNode.flags |= Nif::NiNode::Flag_BBoxCollision; + mNode.boundXYZ = osg::Vec3f(1, 2, 3); + mNode.boundPos = osg::Vec3f(-1, -2, -3); + + mNiNode.hasBounds = true; + mNiNode.boundXYZ = osg::Vec3f(4, 5, 6); + mNiNode.boundPos = osg::Vec3f(-4, -5, -6); + mNiNode.children = Nif::NodeList(std::vector({Nif::NodePtr(&mNode)})); + + EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); + EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); + const auto result = mLoader.load(mNifFile); + + Resource::BulletShape expected; + expected.mCollisionBoxHalfExtents = osg::Vec3f(1, 2, 3); + expected.mCollisionBoxTranslate = osg::Vec3f(-1, -2, -3); + std::unique_ptr box(new btBoxShape(btVector3(1, 2, 3))); + std::unique_ptr shape(new btCompoundShape); + shape->addChildShape(btTransform(btMatrix3x3::getIdentity(), btVector3(-1, -2, -3)), box.release()); + expected.mCollisionShape = shape.release(); + + EXPECT_EQ(*result, expected); + } + + TEST_F(TestBulletNifLoader, for_root_and_two_children_where_both_with_bounds_but_only_first_with_flag_should_use_first_bounds) + { + mNode.hasBounds = true; + mNode.flags |= Nif::NiNode::Flag_BBoxCollision; + mNode.boundXYZ = osg::Vec3f(1, 2, 3); + mNode.boundPos = osg::Vec3f(-1, -2, -3); + + mNode2.hasBounds = true; + mNode2.boundXYZ = osg::Vec3f(4, 5, 6); + mNode2.boundPos = osg::Vec3f(-4, -5, -6); + + mNiNode.hasBounds = true; + mNiNode.boundXYZ = osg::Vec3f(7, 8, 9); + mNiNode.boundPos = osg::Vec3f(-7, -8, -9); + mNiNode.children = Nif::NodeList(std::vector({Nif::NodePtr(&mNode), Nif::NodePtr(&mNode2)})); + + EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); + EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); + const auto result = mLoader.load(mNifFile); + + Resource::BulletShape expected; + expected.mCollisionBoxHalfExtents = osg::Vec3f(1, 2, 3); + expected.mCollisionBoxTranslate = osg::Vec3f(-1, -2, -3); + std::unique_ptr box(new btBoxShape(btVector3(1, 2, 3))); + std::unique_ptr shape(new btCompoundShape); + shape->addChildShape(btTransform(btMatrix3x3::getIdentity(), btVector3(-1, -2, -3)), box.release()); + expected.mCollisionShape = shape.release(); + + EXPECT_EQ(*result, expected); + } + + TEST_F(TestBulletNifLoader, for_root_and_two_children_where_both_with_bounds_but_only_second_with_flag_should_use_second_bounds) + { + mNode.hasBounds = true; + mNode.boundXYZ = osg::Vec3f(1, 2, 3); + mNode.boundPos = osg::Vec3f(-1, -2, -3); + + mNode2.hasBounds = true; + mNode2.flags |= Nif::NiNode::Flag_BBoxCollision; + mNode2.boundXYZ = osg::Vec3f(4, 5, 6); + mNode2.boundPos = osg::Vec3f(-4, -5, -6); + + mNiNode.hasBounds = true; + mNiNode.boundXYZ = osg::Vec3f(7, 8, 9); + mNiNode.boundPos = osg::Vec3f(-7, -8, -9); + mNiNode.children = Nif::NodeList(std::vector({Nif::NodePtr(&mNode), Nif::NodePtr(&mNode2)})); + + EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); + EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); + const auto result = mLoader.load(mNifFile); + + Resource::BulletShape expected; + expected.mCollisionBoxHalfExtents = osg::Vec3f(4, 5, 6); + expected.mCollisionBoxTranslate = osg::Vec3f(-4, -5, -6); + std::unique_ptr box(new btBoxShape(btVector3(4, 5, 6))); + std::unique_ptr shape(new btCompoundShape); + shape->addChildShape(btTransform(btMatrix3x3::getIdentity(), btVector3(-4, -5, -6)), box.release()); + expected.mCollisionShape = shape.release(); + + EXPECT_EQ(*result, expected); + } + + TEST_F(TestBulletNifLoader, for_root_nif_node_with_bounds_but_without_flag_should_return_shape_with_bounds_but_with_null_collision_shape) + { + mNode.hasBounds = true; + mNode.boundXYZ = osg::Vec3f(1, 2, 3); + mNode.boundPos = osg::Vec3f(-1, -2, -3); + + EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); + EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNode)); + EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); + const auto result = mLoader.load(mNifFile); + + Resource::BulletShape expected; + expected.mCollisionBoxHalfExtents = osg::Vec3f(1, 2, 3); + expected.mCollisionBoxTranslate = osg::Vec3f(-1, -2, -3); + + EXPECT_EQ(*result, expected); + } + + TEST_F(TestBulletNifLoader, for_tri_shape_root_node_should_return_shape_with_triangle_mesh_shape) + { + EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); + EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiTriShape)); + EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); + const auto result = mLoader.load(mNifFile); + + std::unique_ptr triangles(new btTriangleMesh(false)); + triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0)); + Resource::BulletShape expected; + expected.mCollisionShape = new Resource::TriangleMeshShape(triangles.release(), true); + + EXPECT_EQ(*result, expected); + } + + TEST_F(TestBulletNifLoader, for_tri_shape_root_node_with_bounds_should_return_shape_with_bounds_but_with_null_collision_shape) + { + mNiTriShape.hasBounds = true; + mNiTriShape.boundXYZ = osg::Vec3f(1, 2, 3); + mNiTriShape.boundPos = osg::Vec3f(-1, -2, -3); + + EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); + EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiTriShape)); + EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); + const auto result = mLoader.load(mNifFile); + + Resource::BulletShape expected; + expected.mCollisionBoxHalfExtents = osg::Vec3f(1, 2, 3); + expected.mCollisionBoxTranslate = osg::Vec3f(-1, -2, -3); + + EXPECT_EQ(*result, expected); + } + + TEST_F(TestBulletNifLoader, for_tri_shape_child_node_should_return_shape_with_triangle_mesh_shape) + { + mNiNode.children = Nif::NodeList(std::vector({Nif::NodePtr(&mNiTriShape)})); + + EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); + EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); + EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); + const auto result = mLoader.load(mNifFile); + + std::unique_ptr triangles(new btTriangleMesh(false)); + triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0)); + Resource::BulletShape expected; + expected.mCollisionShape = new Resource::TriangleMeshShape(triangles.release(), true); + + EXPECT_EQ(*result, expected); + } + + TEST_F(TestBulletNifLoader, for_nested_tri_shape_child_should_return_shape_with_triangle_mesh_shape) + { + mNiNode.children = Nif::NodeList(std::vector({Nif::NodePtr(&mNiNode2)})); + mNiNode2.children = Nif::NodeList(std::vector({Nif::NodePtr(&mNiTriShape)})); + + EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); + EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); + EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); + const auto result = mLoader.load(mNifFile); + + std::unique_ptr triangles(new btTriangleMesh(false)); + triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0)); + Resource::BulletShape expected; + expected.mCollisionShape = new Resource::TriangleMeshShape(triangles.release(), true); + + EXPECT_EQ(*result, expected); + } + + TEST_F(TestBulletNifLoader, for_two_tri_shape_children_should_return_shape_with_triangle_mesh_shape_with_all_meshes) + { + mNiNode.children = Nif::NodeList(std::vector({ + Nif::NodePtr(&mNiTriShape), + Nif::NodePtr(&mNiTriShape2) + })); + + EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); + EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); + EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); + const auto result = mLoader.load(mNifFile); + + std::unique_ptr triangles(new btTriangleMesh(false)); + triangles->addTriangle(btVector3(0, 0, 1), btVector3(1, 0, 1), btVector3(1, 1, 1)); + triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0)); + Resource::BulletShape expected; + expected.mCollisionShape = new Resource::TriangleMeshShape(triangles.release(), true); + + EXPECT_EQ(*result, expected); + } + + TEST_F(TestBulletNifLoader, for_tri_shape_child_node_and_filename_starting_with_x_and_not_empty_skin_should_return_shape_with_triangle_mesh_shape) + { + mNiTriShape.skin = Nif::NiSkinInstancePtr(&mNiSkinInstance); + mNiNode.children = Nif::NodeList(std::vector({Nif::NodePtr(&mNiTriShape)})); + + EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); + EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); + EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("xtest.nif")); + const auto result = mLoader.load(mNifFile); + + std::unique_ptr triangles(new btTriangleMesh(false)); + triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0)); + Resource::BulletShape expected; + expected.mCollisionShape = new Resource::TriangleMeshShape(triangles.release(), true); + + EXPECT_EQ(*result, expected); + } + + TEST_F(TestBulletNifLoader, for_tri_shape_root_node_and_filename_starting_with_x_should_return_shape_with_compound_shape) + { + copy(mTransform, mNiTriShape.trafo); + mNiTriShape.trafo.scale = 3; + + EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); + EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiTriShape)); + EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("xtest.nif")); + const auto result = mLoader.load(mNifFile); + + std::unique_ptr triangles(new btTriangleMesh(false)); + triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0)); + std::unique_ptr mesh(new Resource::TriangleMeshShape(triangles.release(), true)); + mesh->setLocalScaling(btVector3(3, 3, 3)); + std::unique_ptr shape(new btCompoundShape); + shape->addChildShape(mResultTransform, mesh.release()); + Resource::BulletShape expected; + expected.mCollisionShape = shape.release(); + expected.mAnimatedShapes = {{-1, 0}}; + + EXPECT_EQ(*result, expected); + } + + TEST_F(TestBulletNifLoader, for_tri_shape_child_node_and_filename_starting_with_x_should_return_shape_with_compound_shape) + { + copy(mTransform, mNiTriShape.trafo); + mNiTriShape.trafo.scale = 3; + mNiTriShape.parent = &mNiNode; + mNiNode.children = Nif::NodeList(std::vector({Nif::NodePtr(&mNiTriShape)})); + mNiNode.trafo.scale = 4; + + EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); + EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); + EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("xtest.nif")); + const auto result = mLoader.load(mNifFile); + + std::unique_ptr triangles(new btTriangleMesh(false)); + triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0)); + std::unique_ptr mesh(new Resource::TriangleMeshShape(triangles.release(), true)); + mesh->setLocalScaling(btVector3(12, 12, 12)); + std::unique_ptr shape(new btCompoundShape); + shape->addChildShape(mResultTransform2, mesh.release()); + Resource::BulletShape expected; + expected.mCollisionShape = shape.release(); + expected.mAnimatedShapes = {{-1, 0}}; + + EXPECT_EQ(*result, expected); + } + + TEST_F(TestBulletNifLoader, for_two_tri_shape_children_nodes_and_filename_starting_with_x_should_return_shape_with_compound_shape) + { + copy(mTransform, mNiTriShape.trafo); + mNiTriShape.trafo.scale = 3; + + copy(mTransform, mNiTriShape2.trafo); + mNiTriShape2.trafo.scale = 3; + + mNiNode.children = Nif::NodeList(std::vector({ + Nif::NodePtr(&mNiTriShape), + Nif::NodePtr(&mNiTriShape2), + })); + + EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); + EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); + EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("xtest.nif")); + const auto result = mLoader.load(mNifFile); + + std::unique_ptr triangles(new btTriangleMesh(false)); + triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0)); + std::unique_ptr mesh(new Resource::TriangleMeshShape(triangles.release(), true)); + mesh->setLocalScaling(btVector3(3, 3, 3)); + + std::unique_ptr triangles2(new btTriangleMesh(false)); + triangles2->addTriangle(btVector3(0, 0, 1), btVector3(1, 0, 1), btVector3(1, 1, 1)); + std::unique_ptr mesh2(new Resource::TriangleMeshShape(triangles2.release(), true)); + mesh2->setLocalScaling(btVector3(3, 3, 3)); + + std::unique_ptr shape(new btCompoundShape); + shape->addChildShape(mResultTransform, mesh.release()); + shape->addChildShape(mResultTransform, mesh2.release()); + Resource::BulletShape expected; + expected.mCollisionShape = shape.release(); + expected.mAnimatedShapes = {{-1, 0}}; + + EXPECT_EQ(*result, expected); + } + + TEST_F(TestBulletNifLoader, for_tri_shape_child_node_with_controller_should_return_shape_with_compound_shape) + { + mController.recType = Nif::RC_NiKeyframeController; + mController.flags |= Nif::NiNode::ControllerFlag_Active; + copy(mTransform, mNiTriShape.trafo); + mNiTriShape.trafo.scale = 3; + mNiTriShape.parent = &mNiNode; + mNiTriShape.controller = Nif::ControllerPtr(&mController); + mNiNode.children = Nif::NodeList(std::vector({Nif::NodePtr(&mNiTriShape)})); + mNiNode.trafo.scale = 4; + + EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); + EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); + EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); + const auto result = mLoader.load(mNifFile); + + std::unique_ptr triangles(new btTriangleMesh(false)); + triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0)); + std::unique_ptr mesh(new Resource::TriangleMeshShape(triangles.release(), true)); + mesh->setLocalScaling(btVector3(12, 12, 12)); + std::unique_ptr shape(new btCompoundShape); + shape->addChildShape(mResultTransform2, mesh.release()); + Resource::BulletShape expected; + expected.mCollisionShape = shape.release(); + expected.mAnimatedShapes = {{-1, 0}}; + + EXPECT_EQ(*result, expected); + } + + TEST_F(TestBulletNifLoader, for_two_tri_shape_children_nodes_where_one_with_controller_should_return_shape_with_compound_shape) + { + mController.recType = Nif::RC_NiKeyframeController; + mController.flags |= Nif::NiNode::ControllerFlag_Active; + copy(mTransform, mNiTriShape.trafo); + mNiTriShape.trafo.scale = 3; + copy(mTransform, mNiTriShape2.trafo); + mNiTriShape2.trafo.scale = 3; + mNiTriShape2.parent = &mNiNode; + mNiTriShape2.controller = Nif::ControllerPtr(&mController); + mNiNode.children = Nif::NodeList(std::vector({ + Nif::NodePtr(&mNiTriShape), + Nif::NodePtr(&mNiTriShape2), + })); + mNiNode.trafo.scale = 4; + + EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); + EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); + EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); + const auto result = mLoader.load(mNifFile); + + std::unique_ptr triangles(new btTriangleMesh(false)); + triangles->addTriangle(btVector3(1, 2, 3), btVector3(4, 2, 3), btVector3(4, 4.632747650146484375, 1.56172335147857666015625)); + std::unique_ptr mesh(new Resource::TriangleMeshShape(triangles.release(), true)); + mesh->setLocalScaling(btVector3(1, 1, 1)); + + std::unique_ptr triangles2(new btTriangleMesh(false)); + triangles2->addTriangle(btVector3(0, 0, 1), btVector3(1, 0, 1), btVector3(1, 1, 1)); + std::unique_ptr mesh2(new Resource::TriangleMeshShape(triangles2.release(), true)); + mesh2->setLocalScaling(btVector3(12, 12, 12)); + + std::unique_ptr shape(new btCompoundShape); + shape->addChildShape(mResultTransform2, mesh2.release()); + shape->addChildShape(btTransform::getIdentity(), mesh.release()); + Resource::BulletShape expected; + expected.mCollisionShape = shape.release(); + expected.mAnimatedShapes = {{-1, 0}}; + + EXPECT_EQ(*result, expected); + } + + TEST_F(TestBulletNifLoader, for_root_avoid_node_and_tri_shape_child_node_should_return_shape_with_null_collision_shape) + { + mNiNode.children = Nif::NodeList(std::vector({Nif::NodePtr(&mNiTriShape)})); + mNiNode.recType = Nif::RC_AvoidNode; + + EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); + EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); + EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); + const auto result = mLoader.load(mNifFile); + + Resource::BulletShape expected; + + EXPECT_EQ(*result, expected); + } + + TEST_F(TestBulletNifLoader, for_tri_shape_child_node_with_empty_data_should_return_shape_with_null_collision_shape) + { + mNiTriShape.data = Nif::NiTriShapeDataPtr(nullptr); + mNiNode.children = Nif::NodeList(std::vector({Nif::NodePtr(&mNiTriShape)})); + + EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); + EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); + EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); + const auto result = mLoader.load(mNifFile); + + Resource::BulletShape expected; + + EXPECT_EQ(*result, expected); + } + + TEST_F(TestBulletNifLoader, for_tri_shape_child_node_with_empty_data_triangles_should_return_shape_with_null_collision_shape) + { + mNiTriShape.data->triangles.clear(); + mNiNode.children = Nif::NodeList(std::vector({Nif::NodePtr(&mNiTriShape)})); + + EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); + EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); + EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); + const auto result = mLoader.load(mNifFile); + + Resource::BulletShape expected; + + EXPECT_EQ(*result, expected); + } + + TEST_F(TestBulletNifLoader, for_tri_shape_child_node_with_extra_data_string_starting_with_nc_should_return_shape_with_null_collision_shape) + { + mNiStringExtraData.string = "NC___"; + mNiStringExtraData.recType = Nif::RC_NiStringExtraData; + mNiTriShape.extra = Nif::ExtraPtr(&mNiStringExtraData); + mNiNode.children = Nif::NodeList(std::vector({Nif::NodePtr(&mNiTriShape)})); + + EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); + EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); + EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); + const auto result = mLoader.load(mNifFile); + + Resource::BulletShape expected; + + EXPECT_EQ(*result, expected); + } + + TEST_F(TestBulletNifLoader, for_tri_shape_child_node_with_not_first_extra_data_string_starting_with_nc_should_return_shape_with_null_collision_shape) + { + mNiStringExtraData.extra = Nif::ExtraPtr(&mNiStringExtraData2); + mNiStringExtraData2.string = "NC___"; + mNiStringExtraData2.recType = Nif::RC_NiStringExtraData; + mNiTriShape.extra = Nif::ExtraPtr(&mNiStringExtraData); + mNiNode.children = Nif::NodeList(std::vector({Nif::NodePtr(&mNiTriShape)})); + + EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); + EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); + EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); + const auto result = mLoader.load(mNifFile); + + Resource::BulletShape expected; + + EXPECT_EQ(*result, expected); + } + + TEST_F(TestBulletNifLoader, for_tri_shape_child_node_with_extra_data_string_mrk_should_return_shape_with_null_collision_shape) + { + mNiStringExtraData.string = "MRK"; + mNiStringExtraData.recType = Nif::RC_NiStringExtraData; + mNiTriShape.extra = Nif::ExtraPtr(&mNiStringExtraData); + mNiNode.children = Nif::NodeList(std::vector({Nif::NodePtr(&mNiTriShape)})); + + EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); + EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); + EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); + const auto result = mLoader.load(mNifFile); + + Resource::BulletShape expected; + + EXPECT_EQ(*result, expected); + } + + TEST_F(TestBulletNifLoader, for_tri_shape_child_node_with_extra_data_string_mrk_and_other_collision_node_should_return_shape_with_triangle_mesh_shape_with_all_meshes) + { + mNiStringExtraData.string = "MRK"; + mNiStringExtraData.recType = Nif::RC_NiStringExtraData; + mNiTriShape.extra = Nif::ExtraPtr(&mNiStringExtraData); + mNiNode3.children = Nif::NodeList(std::vector({Nif::NodePtr(&mNiTriShape)})); + mNiNode3.recType = Nif::RC_RootCollisionNode; + mNiNode2.children = Nif::NodeList(std::vector({Nif::NodePtr(nullptr), Nif::NodePtr(&mNiNode3)})); + mNiNode2.recType = Nif::RC_NiNode; + mNiNode.children = Nif::NodeList(std::vector({Nif::NodePtr(&mNiNode2)})); + mNiNode.recType = Nif::RC_NiNode; + + EXPECT_CALL(mNifFile, numRoots()).WillOnce(Return(1)); + EXPECT_CALL(mNifFile, getRoot(0)).WillOnce(Return(&mNiNode)); + EXPECT_CALL(mNifFile, getFilename()).WillOnce(Return("test.nif")); + const auto result = mLoader.load(mNifFile); + + std::unique_ptr triangles(new btTriangleMesh(false)); + triangles->addTriangle(btVector3(0, 0, 0), btVector3(1, 0, 0), btVector3(1, 1, 0)); + Resource::BulletShape expected; + expected.mCollisionShape = new Resource::TriangleMeshShape(triangles.release(), true); + + EXPECT_EQ(*result, expected); + } +} diff --git a/components/bullethelpers/processtrianglecallback.hpp b/components/bullethelpers/processtrianglecallback.hpp new file mode 100644 index 000000000..ee005b459 --- /dev/null +++ b/components/bullethelpers/processtrianglecallback.hpp @@ -0,0 +1,34 @@ +#ifndef OPENMW_COMPONENTS_BULLETHELPERS_PROCESSTRIANGLECALLBACK_H +#define OPENMW_COMPONENTS_BULLETHELPERS_PROCESSTRIANGLECALLBACK_H + +#include + +#include + +namespace BulletHelpers +{ + template + class ProcessTriangleCallback : public btTriangleCallback + { + public: + ProcessTriangleCallback(Impl impl) + : mImpl(std::move(impl)) + {} + + void processTriangle(btVector3* triangle, int partId, int triangleIndex) override final + { + return mImpl(triangle, partId, triangleIndex); + } + + private: + Impl mImpl; + }; + + template + ProcessTriangleCallback::type> makeProcessTriangleCallback(Impl&& impl) + { + return ProcessTriangleCallback::type>(std::forward(impl)); + } +} + +#endif diff --git a/components/nif/niffile.hpp b/components/nif/niffile.hpp index 9e39a37ab..cab2e9880 100644 --- a/components/nif/niffile.hpp +++ b/components/nif/niffile.hpp @@ -14,7 +14,30 @@ namespace Nif { -class NIFFile +struct File +{ + virtual ~File() = default; + + virtual void fail(const std::string &msg) const = 0; + + virtual void warn(const std::string &msg) const = 0; + + virtual Record *getRecord(size_t index) const = 0; + + virtual size_t numRecords() const = 0; + + virtual Record *getRoot(size_t index = 0) const = 0; + + virtual size_t numRoots() const = 0; + + virtual void setUseSkinning(bool skinning) = 0; + + virtual bool getUseSkinning() const = 0; + + virtual std::string getFilename() const = 0; +}; + +class NIFFile final : public File { enum NIFVersion { VER_MW = 0x04000002 // Morrowind NIFs @@ -48,14 +71,14 @@ class NIFFile public: /// Used if file parsing fails - void fail(const std::string &msg) const + void fail(const std::string &msg) const override { std::string err = " NIFFile Error: " + msg; err += "\nFile: " + filename; throw std::runtime_error(err); } /// Used when something goes wrong, but not catastrophically so - void warn(const std::string &msg) const + void warn(const std::string &msg) const override { std::cerr << " NIFFile Warning: " << msg < NIFFilePtr; diff --git a/components/nif/recordptr.hpp b/components/nif/recordptr.hpp index 25beaf098..09c380987 100644 --- a/components/nif/recordptr.hpp +++ b/components/nif/recordptr.hpp @@ -23,6 +23,8 @@ class RecordPtrT public: RecordPtrT() : index(-2) {} + RecordPtrT(X* ptr) : ptr(ptr) {} + /// Read the index from the nif void read(NIFStream *nif) { @@ -87,6 +89,12 @@ class RecordListT std::vector list; public: + RecordListT() = default; + + RecordListT(std::vector list) + : list(std::move(list)) + {} + void read(NIFStream *nif) { int len = nif->getInt(); diff --git a/components/nifbullet/bulletnifloader.cpp b/components/nifbullet/bulletnifloader.cpp index 6f8c8f2c0..3f033967c 100644 --- a/components/nifbullet/bulletnifloader.cpp +++ b/components/nifbullet/bulletnifloader.cpp @@ -56,20 +56,20 @@ BulletNifLoader::~BulletNifLoader() { } -osg::ref_ptr BulletNifLoader::load(const Nif::NIFFilePtr& nif) +osg::ref_ptr BulletNifLoader::load(const Nif::File& nif) { mShape = new Resource::BulletShape; mCompoundShape = NULL; mStaticMesh = NULL; - if (nif->numRoots() < 1) + if (nif.numRoots() < 1) { warn("Found no root nodes in NIF."); return mShape; } - Nif::Record *r = nif->getRoot(0); + Nif::Record *r = nif.getRoot(0); assert(r != NULL); Nif::Node *node = dynamic_cast(r); @@ -96,14 +96,15 @@ osg::ref_ptr BulletNifLoader::load(const Nif::NIFFilePtr& { // files with the name convention xmodel.nif usually have keyframes stored in a separate file xmodel.kf (see Animation::addAnimSource). // assume all nodes in the file will be animated - const bool isAnimated = pathFileNameStartsWithX(nif->getFilename()); + const auto filename = nif.getFilename(); + const bool isAnimated = pathFileNameStartsWithX(filename); // If the mesh has RootCollisionNode, attached to actual root node, use it as collision mesh const Nif::Node* rootCollisionNode = getCollisionNode(node); if (rootCollisionNode) - handleNode(nif->getFilename(), rootCollisionNode, 0, false, isAnimated, false); + handleNode(filename, rootCollisionNode, 0, false, isAnimated, false); else - handleNode(nif->getFilename(), node, 0, true, isAnimated, true); + handleNode(filename, node, 0, true, isAnimated, true); if (mCompoundShape) { diff --git a/components/nifbullet/bulletnifloader.hpp b/components/nifbullet/bulletnifloader.hpp index 8fd9cc2a1..22d228d10 100644 --- a/components/nifbullet/bulletnifloader.hpp +++ b/components/nifbullet/bulletnifloader.hpp @@ -50,7 +50,7 @@ public: abort(); } - osg::ref_ptr load(const Nif::NIFFilePtr& file); + osg::ref_ptr load(const Nif::File& file); private: bool findBoundingBox(const Nif::Node* node, int flags = 0); diff --git a/components/resource/bulletshapemanager.cpp b/components/resource/bulletshapemanager.cpp index a3d09311a..622506d6b 100644 --- a/components/resource/bulletshapemanager.cpp +++ b/components/resource/bulletshapemanager.cpp @@ -127,7 +127,7 @@ osg::ref_ptr BulletShapeManager::getShape(const std::string & if (ext == "nif") { NifBullet::BulletNifLoader loader; - shape = loader.load(mNifFileManager->get(normalized)); + shape = loader.load(*mNifFileManager->get(normalized)); } else { From 2de38142e249dfc5d7c9e688ea492480d1012393 Mon Sep 17 00:00:00 2001 From: elsid Date: Wed, 11 Jul 2018 00:13:32 +0300 Subject: [PATCH 23/53] Replace raw pointers by unique_ptr --- components/nifbullet/bulletnifloader.cpp | 31 +++++++++++++++--------- components/nifbullet/bulletnifloader.hpp | 4 +-- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/components/nifbullet/bulletnifloader.cpp b/components/nifbullet/bulletnifloader.cpp index 3f033967c..82d27269f 100644 --- a/components/nifbullet/bulletnifloader.cpp +++ b/components/nifbullet/bulletnifloader.cpp @@ -47,8 +47,8 @@ namespace NifBullet { BulletNifLoader::BulletNifLoader() - : mCompoundShape(NULL) - , mStaticMesh(NULL) + : mCompoundShape() + , mStaticMesh() { } @@ -84,10 +84,11 @@ osg::ref_ptr BulletNifLoader::load(const Nif::File& nif) { std::unique_ptr compound (new btCompoundShape); - btBoxShape* boxShape = new btBoxShape(getbtVector(mShape->mCollisionBoxHalfExtents)); + std::unique_ptr boxShape(new btBoxShape(getbtVector(mShape->mCollisionBoxHalfExtents))); btTransform transform = btTransform::getIdentity(); transform.setOrigin(getbtVector(mShape->mCollisionBoxTranslate)); - compound->addChildShape(transform, boxShape); + compound->addChildShape(transform, boxShape.get()); + boxShape.release(); mShape->mCollisionShape = compound.release(); return mShape; @@ -108,16 +109,20 @@ osg::ref_ptr BulletNifLoader::load(const Nif::File& nif) if (mCompoundShape) { - mShape->mCollisionShape = mCompoundShape; if (mStaticMesh) { btTransform trans; trans.setIdentity(); - mCompoundShape->addChildShape(trans, new Resource::TriangleMeshShape(mStaticMesh,true)); + mCompoundShape->addChildShape(trans, new Resource::TriangleMeshShape(mStaticMesh.get(), true)); + mStaticMesh.release(); } + mShape->mCollisionShape = mCompoundShape.release(); } else if (mStaticMesh) - mShape->mCollisionShape = new Resource::TriangleMeshShape(mStaticMesh,true); + { + mShape->mCollisionShape = new Resource::TriangleMeshShape(mStaticMesh.get(), true); + mStaticMesh.release(); + } return mShape; } @@ -277,9 +282,9 @@ void BulletNifLoader::handleNiTriShape(const Nif::NiTriShape *shape, int flags, if (isAnimated) { if (!mCompoundShape) - mCompoundShape = new btCompoundShape(); + mCompoundShape.reset(new btCompoundShape); - btTriangleMesh* childMesh = new btTriangleMesh(); + std::unique_ptr childMesh(new btTriangleMesh); const Nif::NiTriShapeData *data = shape->data.getPtr(); @@ -297,7 +302,8 @@ void BulletNifLoader::handleNiTriShape(const Nif::NiTriShape *shape, int flags, childMesh->addTriangle(getbtVector(b1), getbtVector(b2), getbtVector(b3)); } - Resource::TriangleMeshShape* childShape = new Resource::TriangleMeshShape(childMesh,true); + std::unique_ptr childShape(new Resource::TriangleMeshShape(childMesh.get(), true)); + childMesh.release(); float scale = shape->trafo.scale; const Nif::Node* parent = shape; @@ -314,12 +320,13 @@ void BulletNifLoader::handleNiTriShape(const Nif::NiTriShape *shape, int flags, mShape->mAnimatedShapes.insert(std::make_pair(shape->recIndex, mCompoundShape->getNumChildShapes())); - mCompoundShape->addChildShape(trans, childShape); + mCompoundShape->addChildShape(trans, childShape.get()); + childShape.release(); } else { if (!mStaticMesh) - mStaticMesh = new btTriangleMesh(false); + mStaticMesh.reset(new btTriangleMesh(false)); // Static shape, just transform all vertices into position const Nif::NiTriShapeData *data = shape->data.getPtr(); diff --git a/components/nifbullet/bulletnifloader.hpp b/components/nifbullet/bulletnifloader.hpp index 22d228d10..44134fd23 100644 --- a/components/nifbullet/bulletnifloader.hpp +++ b/components/nifbullet/bulletnifloader.hpp @@ -61,9 +61,9 @@ private: void handleNiTriShape(const Nif::NiTriShape *shape, int flags, const osg::Matrixf& transform, bool isAnimated); - btCompoundShape* mCompoundShape; + std::unique_ptr mCompoundShape; - btTriangleMesh* mStaticMesh; + std::unique_ptr mStaticMesh; osg::ref_ptr mShape; }; From c2b8c7086a7009362e6d512cc9d4f2ab05d616ed Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 12 Aug 2018 00:52:02 +0300 Subject: [PATCH 24/53] Fix race condition Values are accessed from main thread and cell preloader threads. --- components/terrain/material.cpp | 199 +++++++++++++++++++++----------- 1 file changed, 133 insertions(+), 66 deletions(-) diff --git a/components/terrain/material.cpp b/components/terrain/material.cpp index 722df9507..a0f051524 100644 --- a/components/terrain/material.cpp +++ b/components/terrain/material.cpp @@ -12,66 +12,150 @@ #include +#include -namespace Terrain +namespace { + class BlendmapTexMat + { + public: + static const osg::ref_ptr& value(const int blendmapScale) + { + static BlendmapTexMat instance; + return instance.get(blendmapScale); + } - osg::ref_ptr getBlendmapTexMat(int blendmapScale) + const osg::ref_ptr& get(const int blendmapScale) + { + const std::lock_guard lock(mMutex); + auto texMat = mTexMatMap.find(blendmapScale); + if (texMat == mTexMatMap.end()) + { + osg::Matrixf matrix; + float scale = (blendmapScale/(static_cast(blendmapScale)+1.f)); + matrix.preMultTranslate(osg::Vec3f(0.5f, 0.5f, 0.f)); + matrix.preMultScale(osg::Vec3f(scale, scale, 1.f)); + matrix.preMultTranslate(osg::Vec3f(-0.5f, -0.5f, 0.f)); + // We need to nudge the blendmap to look like vanilla. + // This causes visible seams unless the blendmap's resolution is doubled, but Vanilla also doubles the blendmap, apparently. + matrix.preMultTranslate(osg::Vec3f(1.0f/blendmapScale/4.0f, 1.0f/blendmapScale/4.0f, 0.f)); + + texMat = mTexMatMap.insert(std::make_pair(blendmapScale, new osg::TexMat(matrix))).first; + } + return texMat->second; + } + + private: + std::mutex mMutex; + std::map> mTexMatMap; + }; + + class LayerTexMat { - static std::map > texMatMap; - osg::ref_ptr texMat = texMatMap[blendmapScale]; - if (!texMat) + public: + static const osg::ref_ptr& value(const float layerTileSize) { - osg::Matrixf matrix; - float scale = (blendmapScale/(static_cast(blendmapScale)+1.f)); - matrix.preMultTranslate(osg::Vec3f(0.5f, 0.5f, 0.f)); - matrix.preMultScale(osg::Vec3f(scale, scale, 1.f)); - matrix.preMultTranslate(osg::Vec3f(-0.5f, -0.5f, 0.f)); - // We need to nudge the blendmap to look like vanilla. - // This causes visible seams unless the blendmap's resolution is doubled, but Vanilla also doubles the blendmap, apparently. - matrix.preMultTranslate(osg::Vec3f(1.0f/blendmapScale/4.0f, 1.0f/blendmapScale/4.0f, 0.f)); - - texMat = new osg::TexMat(matrix); - - texMatMap[blendmapScale] = texMat; + static LayerTexMat instance; + return instance.get(layerTileSize); } - return texMat; - } - osg::ref_ptr getLayerTexMat(float layerTileSize) + const osg::ref_ptr& get(const float layerTileSize) + { + const std::lock_guard lock(mMutex); + auto texMat = mTexMatMap.find(layerTileSize); + if (texMat == mTexMatMap.end()) + { + texMat = mTexMatMap.insert(std::make_pair(layerTileSize, + new osg::TexMat(osg::Matrix::scale(osg::Vec3f(layerTileSize, layerTileSize, 1.f))))).first; + } + return texMat->second; + } + + private: + std::mutex mMutex; + std::map> mTexMatMap; + }; + + class EqualDepth { - static std::map > texMatMap; - osg::ref_ptr texMat = texMatMap[layerTileSize]; - if (!texMat) + public: + static const osg::ref_ptr& value() { - texMat = new osg::TexMat(osg::Matrix::scale(osg::Vec3f(layerTileSize,layerTileSize,1.f))); + static EqualDepth instance; + return instance.mValue; + } + + private: + osg::ref_ptr mValue; - texMatMap[layerTileSize] = texMat; + EqualDepth() + : mValue(new osg::Depth) + { + mValue->setFunction(osg::Depth::EQUAL); } - return texMat; - } + }; - osg::ref_ptr getEqualDepth() + class LequalDepth { - static osg::ref_ptr depth; - if (!depth) + public: + static const osg::ref_ptr& value() { - depth = new osg::Depth; - depth->setFunction(osg::Depth::EQUAL); + static LequalDepth instance; + return instance.mValue; } - return depth; - } - osg::ref_ptr getLequalDepth() + + private: + osg::ref_ptr mValue; + + LequalDepth() + : mValue(new osg::Depth) + { + mValue->setFunction(osg::Depth::LEQUAL); + } + }; + + class BlendFunc { - static osg::ref_ptr depth; - if (!depth) + public: + static const osg::ref_ptr& value() { - depth = new osg::Depth; - depth->setFunction(osg::Depth::LEQUAL); + static BlendFunc instance; + return instance.mValue; } - return depth; - } + private: + osg::ref_ptr mValue; + + BlendFunc() + : mValue(new osg::BlendFunc) + { + mValue->setFunction(osg::BlendFunc::SRC_ALPHA, osg::BlendFunc::ONE); + } + }; + + class TexEnvCombine + { + public: + static const osg::ref_ptr& value() + { + static TexEnvCombine instance; + return instance.mValue; + } + + private: + osg::ref_ptr mValue; + + TexEnvCombine() + : mValue(new osg::TexEnvCombine) + { + mValue->setCombine_RGB(osg::TexEnvCombine::REPLACE); + mValue->setSource0_RGB(osg::TexEnvCombine::PREVIOUS); + } + }; +} + +namespace Terrain +{ std::vector > createPasses(bool useShaders, bool forcePerPixelLighting, bool clampLighting, Shader::ShaderManager* shaderManager, const std::vector &layers, const std::vector > &blendmaps, int blendmapScale, float layerTileSize) { @@ -87,16 +171,9 @@ namespace Terrain if (!firstLayer) { - static osg::ref_ptr blendFunc; - if (!blendFunc) - { - blendFunc= new osg::BlendFunc(); - blendFunc->setFunction(osg::BlendFunc::SRC_ALPHA, osg::BlendFunc::ONE); - } stateset->setMode(GL_BLEND, osg::StateAttribute::ON); - stateset->setAttributeAndModes(blendFunc, osg::StateAttribute::ON); - - stateset->setAttributeAndModes(getEqualDepth(), osg::StateAttribute::ON); + stateset->setAttributeAndModes(BlendFunc::value(), osg::StateAttribute::ON); + stateset->setAttributeAndModes(EqualDepth::value(), osg::StateAttribute::ON); } // disable fog if we're the first layer of several - supposed to be completely black if (firstLayer && blendmaps.size() > 0) @@ -105,7 +182,7 @@ namespace Terrain fog->setStart(10000000); fog->setEnd(10000000); stateset->setAttributeAndModes(fog, osg::StateAttribute::OFF|osg::StateAttribute::OVERRIDE); - stateset->setAttributeAndModes(getLequalDepth(), osg::StateAttribute::ON); + stateset->setAttributeAndModes(LequalDepth::value(), osg::StateAttribute::ON); } int texunit = 0; @@ -115,7 +192,7 @@ namespace Terrain stateset->setTextureAttributeAndModes(texunit, it->mDiffuseMap); if (layerTileSize != 1.f) - stateset->setTextureAttributeAndModes(texunit, getLayerTexMat(layerTileSize), osg::StateAttribute::ON); + stateset->setTextureAttributeAndModes(texunit, LayerTexMat::value(layerTileSize), osg::StateAttribute::ON); stateset->addUniform(new osg::Uniform("diffuseMap", texunit)); @@ -125,8 +202,7 @@ namespace Terrain osg::ref_ptr blendmap = blendmaps.at(blendmapIndex++); stateset->setTextureAttributeAndModes(texunit, blendmap.get()); - - stateset->setTextureAttributeAndModes(texunit, getBlendmapTexMat(blendmapScale)); + stateset->setTextureAttributeAndModes(texunit, BlendmapTexMat::value(blendmapScale)); stateset->addUniform(new osg::Uniform("blendMap", texunit)); } @@ -165,17 +241,8 @@ namespace Terrain stateset->setTextureAttributeAndModes(texunit, blendmap.get()); // This is to map corner vertices directly to the center of a blendmap texel. - stateset->setTextureAttributeAndModes(texunit, getBlendmapTexMat(blendmapScale)); - - static osg::ref_ptr texEnvCombine; - if (!texEnvCombine) - { - texEnvCombine = new osg::TexEnvCombine; - texEnvCombine->setCombine_RGB(osg::TexEnvCombine::REPLACE); - texEnvCombine->setSource0_RGB(osg::TexEnvCombine::PREVIOUS); - } - - stateset->setTextureAttributeAndModes(texunit, texEnvCombine, osg::StateAttribute::ON); + stateset->setTextureAttributeAndModes(texunit, BlendmapTexMat::value(blendmapScale)); + stateset->setTextureAttributeAndModes(texunit, TexEnvCombine::value(), osg::StateAttribute::ON); ++texunit; } @@ -185,7 +252,7 @@ namespace Terrain stateset->setTextureAttributeAndModes(texunit, tex.get()); if (layerTileSize != 1.f) - stateset->setTextureAttributeAndModes(texunit, getLayerTexMat(layerTileSize), osg::StateAttribute::ON); + stateset->setTextureAttributeAndModes(texunit, LayerTexMat::value(layerTileSize), osg::StateAttribute::ON); } stateset->setRenderBinDetails(passIndex++, "RenderBin"); From 7d6e3673e0a965754a87f9351a25cba204affcf4 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Fri, 3 Aug 2018 13:19:12 +0400 Subject: [PATCH 25/53] Implement advanced logging system (feature #4581) --- CHANGELOG.md | 1 + apps/opencs/main.cpp | 4 +- apps/openmw/main.cpp | 4 +- components/CMakeLists.txt | 6 +- components/debug/debugging.cpp | 103 +++++++++++++++++++++++++ components/debug/debugging.hpp | 129 ++++++++++++++++++++++++++++++++ components/debug/debuglog.cpp | 8 ++ components/debug/debuglog.hpp | 66 ++++++++++++++++ components/misc/debugging.hpp | 108 -------------------------- components/nifosg/nifloader.cpp | 5 +- 10 files changed, 319 insertions(+), 115 deletions(-) create mode 100644 components/debug/debugging.cpp create mode 100644 components/debug/debugging.hpp create mode 100644 components/debug/debuglog.cpp create mode 100644 components/debug/debuglog.hpp delete mode 100644 components/misc/debugging.hpp diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a6621bf2..f10318ea6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -103,6 +103,7 @@ Feature #4548: Weapon priority: use the actual chance to hit the target instead of weapon skill Feature #4549: Weapon priority: use the actual damage in weapon rating calculations Feature #4550: Weapon priority: make ranged weapon bonus more sensible + Feature #4581: Use proper logging system Task #2490: Don't open command prompt window on Release-mode builds automatically Task #4545: Enable is_pod string test diff --git a/apps/opencs/main.cpp b/apps/opencs/main.cpp index ebc686c23..6058e73f9 100644 --- a/apps/opencs/main.cpp +++ b/apps/opencs/main.cpp @@ -8,7 +8,7 @@ #include #include -#include +#include #include "model/doc/messages.hpp" #include "model/world/universalid.hpp" @@ -80,5 +80,5 @@ int runApplication(int argc, char *argv[]) int main(int argc, char *argv[]) { - return wrapApplication(&runApplication, argc, argv, "/openmw-cs.log"); + return wrapApplication(&runApplication, argc, argv, "OpenMW-CS"); } diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index d9fff6952..bbe1267b1 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -5,7 +5,7 @@ #include #include #include -#include +#include #include "engine.hpp" @@ -265,7 +265,7 @@ extern "C" int SDL_main(int argc, char**argv) int main(int argc, char**argv) #endif { - return wrapApplication(&runApplication, argc, argv, "/openmw.log"); + return wrapApplication(&runApplication, argc, argv, "OpenMW"); } // Platform specific for Windows when there is no console built into the executable. diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index e2e6b97bb..74a11c6c9 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -85,7 +85,11 @@ add_component_dir (esmterrain ) add_component_dir (misc - utf8stream stringops resourcehelpers rng debugging messageformatparser + utf8stream stringops resourcehelpers rng messageformatparser + ) + +add_component_dir (debug + debugging debuglog ) IF(NOT WIN32 AND NOT APPLE) diff --git a/components/debug/debugging.cpp b/components/debug/debugging.cpp new file mode 100644 index 000000000..bec97207a --- /dev/null +++ b/components/debug/debugging.cpp @@ -0,0 +1,103 @@ +#include "debugging.hpp" + +namespace Debug +{ + std::streamsize DebugOutputBase::write(const char *str, std::streamsize size) + { + // Skip debug level marker + Level level = getLevelMarker(str); + if (level != NoLevel) + { + writeImpl(str+1, size-1, level); + return size; + } + + writeImpl(str, size, NoLevel); + return size; + } + + Level DebugOutputBase::getLevelMarker(const char *str) + { + if (unsigned(*str) <= unsigned(Marker)) + { + return Level(*str); + } + + return NoLevel; + } + + void DebugOutputBase::fillCurrentDebugLevel() + { + const char* env = getenv("OPENMW_DEBUG_LEVEL"); + if (env) + { + std::string value(env); + if (value == "ERROR") + CurrentDebugLevel = Error; + else if (value == "WARNING") + CurrentDebugLevel = Warning; + else if (value == "INFO") + CurrentDebugLevel = Info; + else if (value == "VERBOSE") + CurrentDebugLevel = Verbose; + + return; + } + + CurrentDebugLevel = Verbose; + } +} + +int wrapApplication(int (*innerApplication)(int argc, char *argv[]), int argc, char *argv[], const std::string& appName) +{ + // Some objects used to redirect cout and cerr + // Scope must be here, so this still works inside the catch block for logging exceptions + std::streambuf* cout_rdbuf = std::cout.rdbuf (); + std::streambuf* cerr_rdbuf = std::cerr.rdbuf (); + + int ret = 0; + try + { + Files::ConfigurationManager cfgMgr; +#if defined(_WIN32) && defined(_DEBUG) + // Redirect cout and cerr to VS debug output when running in debug mode + boost::iostreams::stream_buffer sb; + sb.open(Debug::DebugOutput()); + std::cout.rdbuf (&sb); + std::cerr.rdbuf (&sb); +#else + // Redirect cout and cerr to the log file + const std::string logName = Misc::StringUtils::lowerCase(appName) + ".log"; + boost::filesystem::ofstream logfile; + logfile.open (boost::filesystem::path(cfgMgr.getLogPath() / logName)); + + boost::iostreams::stream_buffer coutsb; + boost::iostreams::stream_buffer cerrsb; + std::ostream oldcout(cout_rdbuf); + std::ostream oldcerr(cerr_rdbuf); + coutsb.open (Debug::Tee(logfile, oldcout)); + cerrsb.open (Debug::Tee(logfile, oldcerr)); + + std::cout.rdbuf (&coutsb); + std::cerr.rdbuf (&cerrsb); +#endif + ret = innerApplication(argc, argv); + } + catch (std::exception& e) + { +#if (defined(__APPLE__) || defined(__linux) || defined(__unix) || defined(__posix)) + if (!isatty(fileno(stdin))) +#endif + SDL_ShowSimpleMessageBox(0, (appName + ": Fatal error").c_str(), e.what(), NULL); + + Log(Debug::Error) << "ERROR: " << e.what(); + + ret = 1; + } + + // Restore cout and cerr + std::cout.rdbuf(cout_rdbuf); + std::cerr.rdbuf(cerr_rdbuf); + + return ret; +} diff --git a/components/debug/debugging.hpp b/components/debug/debugging.hpp new file mode 100644 index 000000000..59536d685 --- /dev/null +++ b/components/debug/debugging.hpp @@ -0,0 +1,129 @@ +#ifndef DEBUG_DEBUGGING_H +#define DEBUG_DEBUGGING_H + +#include +#include + +#include + +#include + +#include "debuglog.hpp" + +namespace Debug +{ + // ANSI colors for terminal + enum Color + { + Reset = 0, + DarkGray = 90, + Red = 91, + Yellow = 93 + }; + + class DebugOutputBase : public boost::iostreams::sink + { + public: + DebugOutputBase() + { + if (CurrentDebugLevel == NoLevel) + fillCurrentDebugLevel(); + } + + virtual std::streamsize write(const char *str, std::streamsize size); + + protected: + static Level getLevelMarker(const char *str); + + static void fillCurrentDebugLevel(); + + virtual std::streamsize writeImpl(const char *str, std::streamsize size, Level debugLevel) + { + return size; + } + + char mDebugLevel; + }; + +#if defined(_WIN32) && defined(_DEBUG) + class DebugOutput : public DebugOutputBase + { + public: + std::streamsize writeImpl(const char *str, std::streamsize size, Level debugLevel) + { + // Make a copy for null termination + std::string tmp (str, static_cast(size)); + // Write string to Visual Studio Debug output + OutputDebugString (tmp.c_str ()); + return size; + } + + virtual ~DebugOutput() {} + }; +#else + + class Tee : public DebugOutputBase + { + public: + Tee(std::ostream &stream, std::ostream &stream2) + : out(stream), out2(stream2) + { + // TODO: check which stream is stderr? + mUseColor = useColoredOutput(); + + mColors[Error] = Red; + mColors[Warning] = Yellow; + mColors[Info] = Reset; + mColors[Verbose] = DarkGray; + mColors[NoLevel] = Reset; + } + + virtual std::streamsize writeImpl(const char *str, std::streamsize size, Level debugLevel) + { + out.write (str, size); + out.flush(); + + if(mUseColor) + { + out2 << "\033[0;" << mColors[debugLevel] << "m"; + out2.write (str, size); + out2 << "\033[0;" << Reset << "m"; + } + else + { + out2.write(str, size); + } + out2.flush(); + + return size; + } + + virtual ~Tee() {} + + private: + + static bool useColoredOutput() + { + // Note: cmd.exe in Win10 should support ANSI colors, but in its own way. +#if defined(_WIN32) + return 0; +#else + char *term = getenv("TERM"); + bool useColor = term && !getenv("NO_COLOR") && isatty(fileno(stderr)); + + return useColor; +#endif + } + + std::ostream &out; + std::ostream &out2; + bool mUseColor; + + std::map mColors; + }; +#endif +} + +int wrapApplication(int (*innerApplication)(int argc, char *argv[]), int argc, char *argv[], const std::string& appName); + +#endif diff --git a/components/debug/debuglog.cpp b/components/debug/debuglog.cpp new file mode 100644 index 000000000..510c63861 --- /dev/null +++ b/components/debug/debuglog.cpp @@ -0,0 +1,8 @@ +#include "debuglog.hpp" + +namespace Debug +{ + Level CurrentDebugLevel = Level::NoLevel; +} + +std::mutex Log::sLock; diff --git a/components/debug/debuglog.hpp b/components/debug/debuglog.hpp new file mode 100644 index 000000000..bfd7d1196 --- /dev/null +++ b/components/debug/debuglog.hpp @@ -0,0 +1,66 @@ +#ifndef DEBUG_LOG_H +#define DEBUG_LOG_H + +#include +#include +#include + +namespace Debug +{ + enum Level + { + NoLevel = 0, + Error = 1, + Warning = 2, + Info = 3, + Verbose = 4, + Marker = Verbose + }; + + extern Level CurrentDebugLevel; +} + +class Log +{ + static std::mutex sLock; + + std::unique_lock mLock; +public: + // Locks a global lock while the object is alive + Log(Debug::Level level) : + mLock(sLock), + mLevel(level) + { + if (mLevel <= Debug::CurrentDebugLevel) + std::cout << static_cast(mLevel); + } + + // Perfect forwarding wrappers to give the chain of objects to cout + template + Log& operator<<(T&& rhs) + { + if (mLevel <= Debug::CurrentDebugLevel) + std::cout << std::forward(rhs); + + return *this; + } + template + Log& operator<<(const T& rhs) + { + if (mLevel <= Debug::CurrentDebugLevel) + std::cout << std::forward(rhs); + + return *this; + } + + ~Log() + { + if (mLevel <= Debug::CurrentDebugLevel) + std::cout << std::endl; + } + +private: + Debug::Level mLevel; +}; + +#endif diff --git a/components/misc/debugging.hpp b/components/misc/debugging.hpp deleted file mode 100644 index c0c3f5a17..000000000 --- a/components/misc/debugging.hpp +++ /dev/null @@ -1,108 +0,0 @@ -#ifndef MISC_DEBUGGING_H -#define MISC_DEBUGGING_H - -#include -#include - -#include - -namespace Misc -{ -#if defined(_WIN32) && defined(_DEBUG) - - class DebugOutput : public boost::iostreams::sink - { - public: - std::streamsize write(const char *str, std::streamsize size) - { - // Make a copy for null termination - std::string tmp (str, static_cast(size)); - // Write string to Visual Studio Debug output - OutputDebugString (tmp.c_str ()); - return size; - } - }; -#else - class Tee : public boost::iostreams::sink - { - public: - Tee(std::ostream &stream, std::ostream &stream2) - : out(stream), out2(stream2) - { - } - - std::streamsize write(const char *str, std::streamsize size) - { - out.write (str, size); - out.flush(); - out2.write (str, size); - out2.flush(); - return size; - } - - private: - std::ostream &out; - std::ostream &out2; - }; -#endif -} - -int wrapApplication(int (*innerApplication)(int argc, char *argv[]), int argc, char *argv[], const std::string& logName) -{ - // Some objects used to redirect cout and cerr - // Scope must be here, so this still works inside the catch block for logging exceptions - std::streambuf* cout_rdbuf = std::cout.rdbuf (); - std::streambuf* cerr_rdbuf = std::cerr.rdbuf (); - -#if !(defined(_WIN32) && defined(_DEBUG)) - boost::iostreams::stream_buffer coutsb; - boost::iostreams::stream_buffer cerrsb; -#endif - - std::ostream oldcout(cout_rdbuf); - std::ostream oldcerr(cerr_rdbuf); - - boost::filesystem::ofstream logfile; - - int ret = 0; - try - { - Files::ConfigurationManager cfgMgr; -#if defined(_WIN32) && defined(_DEBUG) - // Redirect cout and cerr to VS debug output when running in debug mode - boost::iostreams::stream_buffer sb; - sb.open(Misc::DebugOutput()); - std::cout.rdbuf (&sb); - std::cerr.rdbuf (&sb); -#else - // Redirect cout and cerr to the log file - logfile.open (boost::filesystem::path(cfgMgr.getLogPath() / logName)); - - coutsb.open (Misc::Tee(logfile, oldcout)); - cerrsb.open (Misc::Tee(logfile, oldcerr)); - - std::cout.rdbuf (&coutsb); - std::cerr.rdbuf (&cerrsb); -#endif - ret = innerApplication(argc, argv); - } - catch (std::exception& e) - { -#if (defined(__APPLE__) || defined(__linux) || defined(__unix) || defined(__posix)) - if (!isatty(fileno(stdin))) -#endif - SDL_ShowSimpleMessageBox(0, "OpenMW: Fatal error", e.what(), NULL); - - std::cerr << "\nERROR: " << e.what() << std::endl; - - ret = 1; - } - - // Restore cout and cerr - std::cout.rdbuf(cout_rdbuf); - std::cerr.rdbuf(cerr_rdbuf); - - return ret; -} - -#endif diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 4e7f6d511..5857a3987 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -9,6 +9,7 @@ #include // resource +#include #include #include #include @@ -992,7 +993,7 @@ namespace NifOsg int uvSet = *it; if (uvSet >= (int)data->uvlist.size()) { - std::cerr << "Warning: out of bounds UV set " << uvSet << " on TriShape \"" << triShape->name << "\" in " << mFilename << std::endl; + Log(Debug::Verbose) << "Out of bounds UV set " << uvSet << " on TriShape \"" << triShape->name << "\" in " << mFilename; if (!data->uvlist.empty()) geometry->setTexCoordArray(textureStage, new osg::Vec2Array(data->uvlist[0].size(), &data->uvlist[0][0]), osg::Array::BIND_PER_VERTEX); continue; @@ -1279,7 +1280,7 @@ namespace NifOsg const Nif::NiTexturingProperty::Texture& tex = texprop->textures[i]; if(tex.texture.empty() && texprop->controller.empty()) { - std::cerr << "Warning: texture layer " << i << " is in use but empty in " << mFilename << std::endl; + Log(Debug::Verbose) << "Texture layer " << i << " is in use but empty in " << mFilename; continue; } From 307e0103dc5ddcdc87d7c5ac5b7a8390b9b6a662 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Thu, 19 Jul 2018 16:38:32 +0400 Subject: [PATCH 26/53] Use fallbacks for missing weapon animations (bug #4470) --- CHANGELOG.md | 1 + apps/openmw/mwmechanics/character.cpp | 123 ++++++++++++++++++++------ apps/openmw/mwmechanics/character.hpp | 3 + 3 files changed, 101 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 775f0545d..5b70649d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -62,6 +62,7 @@ Bug #4461: "Open" spell from non-player caster isn't a crime Bug #4464: OpenMW keeps AiState cached storages even after we cancel AI packages Bug #4469: Abot Silt Striders – Model turn 90 degrees on horizontal + Bug #4470: Non-bipedal creatures with Weapon & Shield flag have inconsistent behaviour Bug #4474: No fallback when getVampireHead fails Bug #4475: Scripted animations should not cause movement Bug #4479: "Game" category on Advanced page is getting too long diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index db9a53f82..6f9cb941d 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -583,7 +583,7 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat refreshHitRecoilAnims(); const WeaponInfo *weap = std::find_if(sWeaponTypeList, sWeaponTypeListEnd, FindWeaponType(mWeaponType)); - if (!mPtr.getClass().isBipedal(mPtr)) + if (!mPtr.getClass().hasInventoryStore(mPtr)) weap = sWeaponTypeListEnd; refreshJumpAnims(weap, jump, force); @@ -592,7 +592,7 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat // idle handled last as it can depend on the other states // FIXME: if one of the below states is close to their last animation frame (i.e. will be disabled in the coming update), // the idle animation should be displayed - if ((mUpperBodyState != UpperCharState_Nothing + if (((mUpperBodyState != UpperCharState_Nothing && mUpperBodyState != UpperCharState_WeapEquiped) || (mMovementState != CharState_None && !isTurning()) || mHitState != CharState_None) && !mPtr.getClass().isBipedal(mPtr)) @@ -773,6 +773,20 @@ void CharacterController::playRandomDeath(float startpoint) playDeath(startpoint, mDeathState); } +std::string CharacterController::chooseRandomAttackAnimation() const +{ + std::string result; + bool isSwimming = MWBase::Environment::get().getWorld()->isSwimming(mPtr); + + if (isSwimming) + result = chooseRandomGroup("swimattack"); + + if (!isSwimming || !mAnimation->hasAnimation(result)) + result = chooseRandomGroup("attack"); + + return result; +} + CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim) : mPtr(ptr) , mWeapon(MWWorld::Ptr()) @@ -1123,16 +1137,10 @@ bool CharacterController::updateCreatureState() else mCurrentWeapon = ""; } + if (weapType != WeapType_Spell || !mAnimation->hasAnimation("spellcast")) // Not all creatures have a dedicated spellcast animation { - bool isSwimming = MWBase::Environment::get().getWorld()->isSwimming(mPtr); - int roll = Misc::Rng::rollDice(3); // [0, 2] - if (roll == 0) - mCurrentWeapon = isSwimming && mAnimation->hasAnimation("swimattack1") ? "swimattack1" : "attack1"; - else if (roll == 1) - mCurrentWeapon = isSwimming && mAnimation->hasAnimation("swimattack2") ? "swimattack2" : "attack2"; - else - mCurrentWeapon = isSwimming && mAnimation->hasAnimation("swimattack3") ? "swimattack3" : "attack3"; + mCurrentWeapon = chooseRandomAttackAnimation(); } if (!mCurrentWeapon.empty()) @@ -1212,9 +1220,10 @@ bool CharacterController::updateWeaponState() mWeapon = weapon != inv.end() ? *weapon : MWWorld::Ptr(); } - // Apply 1st-person weapon animations only for upper body + // Use blending only with 3d-person movement animations for bipedal actors + bool firstPersonPlayer = (mPtr == MWMechanics::getPlayer() && MWBase::Environment::get().getWorld()->isFirstPerson()); MWRender::Animation::AnimPriority priorityWeapon(Priority_Weapon); - if (mPtr != MWMechanics::getPlayer() || !MWBase::Environment::get().getWorld()->isFirstPerson()) + if (!firstPersonPlayer && mPtr.getClass().isBipedal(mPtr)) priorityWeapon[MWRender::Animation::BoneGroup_LowerBody] = Priority_WeaponLowerBody; bool forcestateupdate = false; @@ -1241,6 +1250,10 @@ bool CharacterController::updateWeaponState() MWRender::Animation::BlendMask_All, false, 1.0f, "unequip start", "unequip stop", 0.0f, 0); mUpperBodyState = UpperCharState_UnEquipingWeap; + + // If we do not have the "unequip detach" key, hide weapon manually. + if (mAnimation->getTextKeyTime(weapgroup+": unequip detach") < 0) + mAnimation->showWeapons(false); } if(!downSoundId.empty()) @@ -1252,7 +1265,6 @@ bool CharacterController::updateWeaponState() float complete; bool animPlaying = mAnimation->getInfo(mCurrentWeapon, &complete); - if (!animPlaying || complete >= 1.0f) { // Weapon is changed, no current animation (e.g. unequipping or attack). @@ -1275,6 +1287,13 @@ bool CharacterController::updateWeaponState() MWRender::Animation::BlendMask_All, true, 1.0f, "equip start", "equip stop", 0.0f, 0); mUpperBodyState = UpperCharState_EquipingWeap; + + // If we do not have the "equip attach" key, show weapon manually. + if (weaptype != WeapType_Spell) + { + if (mAnimation->getTextKeyTime(weapgroup+": equip attach") < 0) + mAnimation->showWeapons(true); + } } } @@ -1365,6 +1384,15 @@ bool CharacterController::updateWeaponState() { MWBase::Environment::get().getWorld()->breakInvisibility(mPtr); mAttackStrength = 0; + + // Randomize attacks for non-bipedal creatures with Weapon flag + if (mPtr.getClass().getTypeName() == typeid(ESM::Creature).name() && + !mPtr.getClass().isBipedal(mPtr) && + (!mAnimation->hasAnimation(mCurrentWeapon) || isRandomAttackAnimation(mCurrentWeapon))) + { + mCurrentWeapon = chooseRandomAttackAnimation(); + } + if(mWeaponType == WeapType_Spell) { // Unset casting flag, otherwise pressing the mouse button down would @@ -1412,16 +1440,31 @@ bool CharacterController::updateWeaponState() const ESM::ENAMstruct &firstEffect = spell->mEffects.mList.at(0); // first effect used for casting animation - switch(firstEffect.mRange) + std::string startKey; + std::string stopKey; + if (isRandomAttackAnimation(mCurrentWeapon)) + { + startKey = "start"; + stopKey = "stop"; + MWBase::Environment::get().getWorld()->castSpell(mPtr, mCastingManualSpell); // No "release" text key to use, so cast immediately + mCastingManualSpell = false; + } + else { - case 0: mAttackType = "self"; break; - case 1: mAttackType = "touch"; break; - case 2: mAttackType = "target"; break; + switch(firstEffect.mRange) + { + case 0: mAttackType = "self"; break; + case 1: mAttackType = "touch"; break; + case 2: mAttackType = "target"; break; + } + + startKey = mAttackType+" start"; + stopKey = mAttackType+" stop"; } mAnimation->play(mCurrentWeapon, priorityWeapon, MWRender::Animation::BlendMask_All, true, - weapSpeed, mAttackType+" start", mAttackType+" stop", + 1, startKey, stopKey, 0.0f, 0); mUpperBodyState = UpperCharState_CastingSpell; } @@ -1469,16 +1512,27 @@ bool CharacterController::updateWeaponState() } else if (ammunition) { - if(mWeaponType == WeapType_Crossbow || mWeaponType == WeapType_BowAndArrow || - mWeaponType == WeapType_Thrown) + std::string startKey; + std::string stopKey; + if(mWeaponType == WeapType_Crossbow || mWeaponType == WeapType_BowAndArrow || mWeaponType == WeapType_Thrown) + { mAttackType = "shoot"; - else + startKey = mAttackType+" start"; + stopKey = mAttackType+" min attack"; + } + + if (isRandomAttackAnimation(mCurrentWeapon)) + { + startKey = "start"; + stopKey = "stop"; + } + else if (mAttackType != "shoot") { if(mPtr == getPlayer()) { if (isWeapon) { - if (Settings::Manager::getBool("best attack", "Game")) + if (Settings::Manager::getBool("best attack", "Game")) { MWWorld::ConstContainerStoreIterator weapon = mPtr.getClass().getInventoryStore(mPtr).getSlot(MWWorld::InventoryStore::Slot_CarriedRight); mAttackType = getBestAttack(weapon->get()->mBase); @@ -1487,14 +1541,19 @@ bool CharacterController::updateWeaponState() setAttackTypeBasedOnMovement(); } else - setAttackTypeRandomly(mAttackType); + { + // There is no "best attack" for Hand-to-Hand + setAttackTypeRandomly(mAttackType); + } } // else if (mPtr != getPlayer()) use mAttackType set by AiCombat + startKey = mAttackType+" start"; + stopKey = mAttackType+" min attack"; } mAnimation->play(mCurrentWeapon, priorityWeapon, MWRender::Animation::BlendMask_All, false, - weapSpeed, mAttackType+" start", mAttackType+" min attack", + weapSpeed, startKey, stopKey, 0.0f, 0); mUpperBodyState = UpperCharState_StartToMinAttack; } @@ -1619,7 +1678,7 @@ bool CharacterController::updateWeaponState() else if(mUpperBodyState == UpperCharState_UnEquipingWeap) mUpperBodyState = UpperCharState_Nothing; } - else if(complete >= 1.0f) + else if(complete >= 1.0f && !isRandomAttackAnimation(mCurrentWeapon)) { std::string start, stop; switch(mUpperBodyState) @@ -1690,6 +1749,11 @@ bool CharacterController::updateWeaponState() weapSpeed, start, stop, 0.0f, 0); } } + else if(complete >= 1.0f && isRandomAttackAnimation(mCurrentWeapon)) + { + mAnimation->disable(mCurrentWeapon); + mUpperBodyState = UpperCharState_WeapEquiped; + } if (mPtr.getClass().hasInventoryStore(mPtr)) { @@ -2422,6 +2486,13 @@ void CharacterController::setAttackTypeBasedOnMovement() mAttackType = "chop"; } +bool CharacterController::isRandomAttackAnimation(const std::string& group) const +{ + return (group == "attack1" || group == "swimattack1" || + group == "attack2" || group == "swimattack2" || + group == "attack3" || group == "swimattack3"); +} + bool CharacterController::isAttackPrepairing() const { return mUpperBodyState == UpperCharState_StartToMinAttack || @@ -2523,7 +2594,7 @@ void CharacterController::setAttackTypeRandomly(std::string& attackType) bool CharacterController::readyToPrepareAttack() const { return (mHitState == CharState_None || mHitState == CharState_Block) - && mUpperBodyState <= UpperCharState_WeapEquiped; + && mUpperBodyState <= UpperCharState_WeapEquiped; } bool CharacterController::readyToStartAttack() const diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 754f551f9..631f78208 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -224,6 +224,9 @@ class CharacterController : public MWRender::Animation::TextKeyListener bool updateCreatureState(); void updateIdleStormState(bool inwater); + std::string chooseRandomAttackAnimation() const; + bool isRandomAttackAnimation(const std::string& group) const; + bool isPersistentAnimPlaying(); void updateAnimQueue(); From 8de3383612f83060492ceefb90e6d3313ed58d28 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sun, 12 Aug 2018 17:04:39 +0400 Subject: [PATCH 27/53] Add zero-duration spells effects to effect list (bug #3533) --- CHANGELOG.md | 1 + apps/openmw/mwmechanics/spellcasting.cpp | 16 +++++++++++----- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 775f0545d..72dd1df3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ Bug #3249: Fixed revert function not updating views properly Bug #3374: Touch spells not hitting kwama foragers Bug #3486: [Mod] NPC Commands does not work + Bug #3533: GetSpellEffects should detect effects with zero duration Bug #3591: Angled hit distance too low Bug #3629: DB assassin attack never triggers creature spawning Bug #3876: Landscape texture painting is misaligned diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 09dadcaba..b337fa6b7 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -509,9 +509,18 @@ namespace MWMechanics } else // target.getClass().isActor() == true { + ActiveSpells::ActiveEffect effect; + effect.mEffectId = effectIt->mEffectID; + effect.mArg = MWMechanics::EffectKey(*effectIt).mArg; + effect.mMagnitude = magnitude; + bool hasDuration = !(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration); if (hasDuration && effectIt->mDuration == 0) { + // We still should add effect to list to allow GetSpellEffects to detect this spell + effect.mDuration = 0.f; + appliedLastingEffects.push_back(effect); + // duration 0 means apply full magnitude instantly bool wasDead = target.getClass().getCreatureStats(target).isDead(); effectTick(target.getClass().getCreatureStats(target), target, EffectKey(*effectIt), magnitude); @@ -522,18 +531,15 @@ namespace MWMechanics } else { - // add to list of active effects, to apply in next frame - ActiveSpells::ActiveEffect effect; - effect.mEffectId = effectIt->mEffectID; - effect.mArg = MWMechanics::EffectKey(*effectIt).mArg; + if (!hasDuration) effect.mDuration = 1.0f; else effect.mDuration = static_cast(effectIt->mDuration); - effect.mMagnitude = magnitude; targetEffects.add(MWMechanics::EffectKey(*effectIt), MWMechanics::EffectParam(effect.mMagnitude)); + // add to list of active effects, to apply in next frame appliedLastingEffects.push_back(effect); // Unequip all items, if a spell with the ExtraSpell effect was casted From 9dfd775bf23cf6421408d017f3e3dbd4c0b9611e Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Mon, 13 Aug 2018 08:30:50 +0400 Subject: [PATCH 28/53] Implement GetPCTraveling console command --- apps/openmw/mwbase/world.hpp | 3 +++ apps/openmw/mwgui/travelwindow.cpp | 4 ++++ apps/openmw/mwscript/miscextensions.cpp | 3 +-- apps/openmw/mwworld/worldimp.cpp | 17 ++++++++++++++++- apps/openmw/mwworld/worldimp.hpp | 4 ++++ 5 files changed, 28 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index a88616625..ee1227e0c 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -566,6 +566,9 @@ namespace MWBase virtual bool isPlayerInJail() const = 0; + virtual void setPlayerTraveling(bool traveling) = 0; + virtual bool isPlayerTraveling() const = 0; + virtual void rotateWorldObject (const MWWorld::Ptr& ptr, osg::Quat rotate) = 0; /// Return terrain height at \a worldPos position. diff --git a/apps/openmw/mwgui/travelwindow.cpp b/apps/openmw/mwgui/travelwindow.cpp index 7a4a9293c..cf4fb1b5e 100644 --- a/apps/openmw/mwgui/travelwindow.cpp +++ b/apps/openmw/mwgui/travelwindow.cpp @@ -154,6 +154,10 @@ namespace MWGui if (playerGoldsetPlayerTraveling(true); + if (!mPtr.getCell()->isExterior()) // Interior cell -> mages guild transport MWBase::Environment::get().getWindowManager()->playSound("mysticism cast"); diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 30d0c6fee..7da1a4833 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -1155,8 +1155,7 @@ namespace MWScript virtual void execute (Interpreter::Runtime &runtime) { - /// \todo implement traveling check - runtime.push (0); + runtime.push (MWBase::Environment::get().getWorld()->isPlayerTraveling()); } }; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index f4c2a75f3..41ed74000 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -153,7 +153,8 @@ namespace MWWorld mGodMode(false), mScriptsEnabled(true), mContentFiles (contentFiles), mUserDataPath(userDataPath), mActivationDistanceOverride (activationDistanceOverride), mStartupScript(startupScript), mStartCell (startCell), mDistanceToFacedObject(-1), mTeleportEnabled(true), - mLevitationEnabled(true), mGoToJail(false), mDaysInPrison(0), mSpellPreloadTimer(0.f) + mLevitationEnabled(true), mGoToJail(false), mDaysInPrison(0), + mPlayerTraveling(false), mSpellPreloadTimer(0.f) { mPhysics.reset(new MWPhysics::PhysicsSystem(resourceSystem, rootNode)); mRendering.reset(new MWRender::RenderingManager(viewer, rootNode, resourceSystem, workQueue, &mFallback, resourcePath)); @@ -311,6 +312,7 @@ namespace MWWorld mGoToJail = false; mTeleportEnabled = true; mLevitationEnabled = true; + mPlayerTraveling = false; fillGlobalVariables(); } @@ -1639,6 +1641,9 @@ namespace MWWorld void World::update (float duration, bool paused) { + // Reset "traveling" flag - there was a frame to detect traveling. + mPlayerTraveling = false; + if (mGoToJail && !paused) goToJail(); @@ -3312,6 +3317,16 @@ namespace MWWorld return MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_Jail); } + void World::setPlayerTraveling(bool traveling) + { + mPlayerTraveling = traveling; + } + + bool World::isPlayerTraveling() const + { + return mPlayerTraveling; + } + float World::getTerrainHeightAt(const osg::Vec3f& worldPos) const { return mRendering->getTerrainHeightAt(worldPos); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index a2616995a..102683768 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -168,6 +168,7 @@ namespace MWWorld bool mLevitationEnabled; bool mGoToJail; int mDaysInPrison; + bool mPlayerTraveling; float mSpellPreloadTimer; @@ -672,6 +673,9 @@ namespace MWWorld bool isPlayerInJail() const override; + void setPlayerTraveling(bool traveling); + bool isPlayerTraveling() const; + /// Return terrain height at \a worldPos position. float getTerrainHeightAt(const osg::Vec3f& worldPos) const override; From 4003fa12962fa0c48a8b2780b340b8068fd552cb Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Mon, 13 Aug 2018 11:10:01 +0400 Subject: [PATCH 29/53] Keep jailing state for one frame after leaving jail (bug #3788) --- CHANGELOG.md | 1 + apps/openmw/mwworld/worldimp.cpp | 19 ++++++++++++------- apps/openmw/mwworld/worldimp.hpp | 5 +++-- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 775f0545d..f08d05d9d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ Bug #3486: [Mod] NPC Commands does not work Bug #3591: Angled hit distance too low Bug #3629: DB assassin attack never triggers creature spawning + Bug #3788: GetPCInJail and GetPCTraveling do not work as in vanilla Bug #3876: Landscape texture painting is misaligned Bug #3897: Have Goodbye give all choices the effects of Goodbye Bug #3911: [macOS] Typing in the "Content List name" dialog box produces double characters diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 41ed74000..874478b71 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -154,7 +154,7 @@ namespace MWWorld mActivationDistanceOverride (activationDistanceOverride), mStartupScript(startupScript), mStartCell (startCell), mDistanceToFacedObject(-1), mTeleportEnabled(true), mLevitationEnabled(true), mGoToJail(false), mDaysInPrison(0), - mPlayerTraveling(false), mSpellPreloadTimer(0.f) + mPlayerTraveling(false), mPlayerInJail(false), mSpellPreloadTimer(0.f) { mPhysics.reset(new MWPhysics::PhysicsSystem(resourceSystem, rootNode)); mRendering.reset(new MWRender::RenderingManager(viewer, rootNode, resourceSystem, workQueue, &mFallback, resourcePath)); @@ -313,6 +313,7 @@ namespace MWWorld mTeleportEnabled = true; mLevitationEnabled = true; mPlayerTraveling = false; + mPlayerInJail = false; fillGlobalVariables(); } @@ -1641,11 +1642,17 @@ namespace MWWorld void World::update (float duration, bool paused) { + if (mGoToJail && !paused) + goToJail(); + // Reset "traveling" flag - there was a frame to detect traveling. mPlayerTraveling = false; - if (mGoToJail && !paused) - goToJail(); + // The same thing for "in jail" flag: reset it if: + // 1. Player was in jail + // 2. Jailing window was closed + if (mPlayerInJail && !mGoToJail && !MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_Jail)) + mPlayerInJail = false; updateWeather(duration, paused); @@ -3286,6 +3293,7 @@ namespace MWWorld { // Reset bounty and forget the crime now, but don't change cell yet (the player should be able to read the dialog text first) mGoToJail = true; + mPlayerInJail = true; MWWorld::Ptr player = getPlayerPtr(); @@ -3311,10 +3319,7 @@ namespace MWWorld bool World::isPlayerInJail() const { - if (mGoToJail) - return true; - - return MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_Jail); + return mPlayerInJail; } void World::setPlayerTraveling(bool traveling) diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 102683768..2352fd31c 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -169,6 +169,7 @@ namespace MWWorld bool mGoToJail; int mDaysInPrison; bool mPlayerTraveling; + bool mPlayerInJail; float mSpellPreloadTimer; @@ -673,8 +674,8 @@ namespace MWWorld bool isPlayerInJail() const override; - void setPlayerTraveling(bool traveling); - bool isPlayerTraveling() const; + void setPlayerTraveling(bool traveling) override; + bool isPlayerTraveling() const override; /// Return terrain height at \a worldPos position. float getTerrainHeightAt(const osg::Vec3f& worldPos) const override; From 2cc1b52baf1a484a6c7a3a83581b00a155b142ec Mon Sep 17 00:00:00 2001 From: Allofich Date: Thu, 24 Nov 2016 00:58:30 +0900 Subject: [PATCH 30/53] Drain and fortify fixes for dynamicStats (Fixes #3049) --- apps/openmw/mwmechanics/actors.cpp | 23 ++++--- apps/openmw/mwmechanics/creaturestats.cpp | 1 + apps/openmw/mwmechanics/stat.cpp | 75 ++++++++++++++++++++--- apps/openmw/mwmechanics/stat.hpp | 23 +++++-- apps/openmw/mwscript/statsextensions.cpp | 1 + 5 files changed, 103 insertions(+), 20 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 2d535f57b..e68a5d398 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -546,7 +546,7 @@ namespace MWMechanics float diff = (static_cast(magickaFactor*intelligence)) - magicka.getBase(); float currentToBaseRatio = (magicka.getCurrent() / magicka.getBase()); magicka.setModified(magicka.getModified() + diff, 0); - magicka.setCurrent(magicka.getBase() * currentToBaseRatio); + magicka.setCurrent(magicka.getBase() * currentToBaseRatio, false, true); creatureStats.setMagicka(magicka); } @@ -577,8 +577,14 @@ namespace MWMechanics float normalizedEncumbrance = ptr.getClass().getNormalizedEncumbrance(ptr); if (normalizedEncumbrance > 1) normalizedEncumbrance = 1; + + // Current fatigue can be above base value due to a fortify effect. + // In that case stop here and don't try to restore. + DynamicStat fatigue = stats.getFatigue(); + if (fatigue.getCurrent() >= fatigue.getBase()) + return; - // restore fatigue + // Restore fatigue float fFatigueReturnBase = settings.find("fFatigueReturnBase")->getFloat (); float fFatigueReturnMult = settings.find("fFatigueReturnMult")->getFloat (); float fEndFatigueMult = settings.find("fEndFatigueMult")->getFloat (); @@ -586,7 +592,6 @@ namespace MWMechanics float x = fFatigueReturnBase + fFatigueReturnMult * (1 - normalizedEncumbrance); x *= fEndFatigueMult * endurance; - DynamicStat fatigue = stats.getFatigue(); fatigue.setCurrent (fatigue.getCurrent() + 3600 * x); stats.setFatigue (fatigue); } @@ -598,16 +603,20 @@ namespace MWMechanics MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats (ptr); - int endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified (); + // Current fatigue can be above base value due to a fortify effect. + // In that case stop here and don't try to restore. + DynamicStat fatigue = stats.getFatigue(); + if (fatigue.getCurrent() >= fatigue.getBase()) + return; - // restore fatigue + // Restore fatigue + int endurance = stats.getAttribute(ESM::Attribute::Endurance).getModified(); const MWWorld::Store& settings = MWBase::Environment::get().getWorld()->getStore().get(); static const float fFatigueReturnBase = settings.find("fFatigueReturnBase")->getFloat (); static const float fFatigueReturnMult = settings.find("fFatigueReturnMult")->getFloat (); float x = fFatigueReturnBase + fFatigueReturnMult * endurance; - DynamicStat fatigue = stats.getFatigue(); fatigue.setCurrent (fatigue.getCurrent() + duration * x); stats.setFatigue (fatigue); } @@ -723,7 +732,7 @@ namespace MWMechanics for(int i = 0;i < 3;++i) { DynamicStat stat = creatureStats.getDynamic(i); - stat.setModifier(effects.get(ESM::MagicEffect::FortifyHealth+i).getMagnitude() - + stat.setCurrentModifier(effects.get(ESM::MagicEffect::FortifyHealth+i).getMagnitude() - effects.get(ESM::MagicEffect::DrainHealth+i).getMagnitude(), // Magicka can be decreased below zero due to a fortify effect wearing off // Fatigue can be decreased below zero meaning the actor will be knocked out diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 49823d7a5..d1128860c 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -195,6 +195,7 @@ namespace MWMechanics mDead = true; mDynamic[index].setModifier(0); + mDynamic[index].setCurrentModifier(0); mDynamic[index].setCurrent(0); if (MWBase::Environment::get().getWorld()->getGodModeState()) diff --git a/apps/openmw/mwmechanics/stat.cpp b/apps/openmw/mwmechanics/stat.cpp index 41c5bac5a..f53052a28 100644 --- a/apps/openmw/mwmechanics/stat.cpp +++ b/apps/openmw/mwmechanics/stat.cpp @@ -5,11 +5,11 @@ namespace MWMechanics { template - Stat::Stat() : mBase (0), mModified (0) {} + Stat::Stat() : mBase (0), mModified (0), mCurrentModified (0) {} template - Stat::Stat(T base) : mBase (base), mModified (base) {} + Stat::Stat(T base) : mBase (base), mModified (base), mCurrentModified (base) {} template - Stat::Stat(T base, T modified) : mBase (base), mModified (modified) {} + Stat::Stat(T base, T modified) : mBase (base), mModified (modified), mCurrentModified (modified) {} template const T& Stat::getBase() const @@ -22,23 +22,42 @@ namespace MWMechanics { return std::max(static_cast(0), mModified); } + + template + T Stat::getCurrentModified() const + { + return mCurrentModified; + } + template T Stat::getModifier() const { return mModified-mBase; } + + template + T Stat::getCurrentModifier() const + { + return mCurrentModified - mModified; + } + template void Stat::set (const T& value) { + T diff = value - mBase; mBase = mModified = value; + mCurrentModified += diff; } + template void Stat::setBase (const T& value) { T diff = value - mBase; mBase = value; mModified += diff; + mCurrentModified += diff; } + template void Stat::setModified (T value, const T& min, const T& max) { @@ -57,24 +76,39 @@ namespace MWMechanics mModified = value; mBase += diff; + mCurrentModified += diff; } + + template + void Stat::setCurrentModified(T value) + { + mCurrentModified = value; + } + template void Stat::setModifier (const T& modifier) { mModified = mBase + modifier; } + template + void Stat::setCurrentModifier(const T& modifier) + { + mCurrentModified = mModified + modifier; + } + template void Stat::writeState (ESM::StatState& state) const { state.mBase = mBase; - state.mMod = mModified; + state.mMod = mCurrentModified; } template void Stat::readState (const ESM::StatState& state) { mBase = state.mBase; - mModified = state.mMod; + mModified = state.mBase; + mCurrentModified = state.mMod; } @@ -98,6 +132,12 @@ namespace MWMechanics { return mStatic.getModified(); } + template + T DynamicStat::getCurrentModified() const + { + return mStatic.getCurrentModified(); + } + template const T& DynamicStat::getCurrent() const { @@ -127,14 +167,21 @@ namespace MWMechanics mCurrent = getModified(); } template - void DynamicStat::setCurrent (const T& value, bool allowDecreaseBelowZero) + void DynamicStat::setCurrentModified(T value) + { + mStatic.setCurrentModified(value); + } + template + void DynamicStat::setCurrent (const T& value, bool allowDecreaseBelowZero, bool allowIncreaseAboveModified) { if (value > mCurrent) { // increase - mCurrent = value; - - if (mCurrent > getModified()) + if (value <= getModified() || allowIncreaseAboveModified) + mCurrent = value; + else if (mCurrent > getModified()) + return; + else mCurrent = getModified(); } else if (value > 0 || allowDecreaseBelowZero) @@ -156,6 +203,16 @@ namespace MWMechanics setCurrent (getCurrent()+diff, allowCurrentDecreaseBelowZero); } + template + void DynamicStat::setCurrentModifier(const T& modifier, bool allowCurrentDecreaseBelowZero) + { + T diff = modifier - mStatic.getCurrentModifier(); + mStatic.setCurrentModifier(modifier); + + // The (modifier > 0) check here allows increase over modified only if the modifier is positive (a fortify effect is active). + setCurrent (getCurrent() + diff, allowCurrentDecreaseBelowZero, (modifier > 0)); + } + template void DynamicStat::writeState (ESM::StatState& state) const { diff --git a/apps/openmw/mwmechanics/stat.hpp b/apps/openmw/mwmechanics/stat.hpp index 5b5800634..751b80b9c 100644 --- a/apps/openmw/mwmechanics/stat.hpp +++ b/apps/openmw/mwmechanics/stat.hpp @@ -17,6 +17,7 @@ namespace MWMechanics { T mBase; T mModified; + T mCurrentModified; public: typedef T Type; @@ -28,7 +29,9 @@ namespace MWMechanics const T& getBase() const; T getModified() const; + T getCurrentModified() const; T getModifier() const; + T getCurrentModifier() const; /// Set base and modified to \a value. void set (const T& value); @@ -36,9 +39,15 @@ namespace MWMechanics /// Set base and adjust modified accordingly. void setBase (const T& value); - /// Set modified value an adjust base accordingly. + /// Set modified value and adjust base accordingly. void setModified (T value, const T& min, const T& max = std::numeric_limits::max()); + + /// Set "current modified," used for drain and fortify. Unlike the regular modifier + /// this just adds and subtracts from the current value without changing the maximum. + void setCurrentModified(T value); + void setModifier (const T& modifier); + void setCurrentModifier (const T& modifier); void writeState (ESM::StatState& state) const; void readState (const ESM::StatState& state); @@ -73,6 +82,7 @@ namespace MWMechanics const T& getBase() const; T getModified() const; + T getCurrentModified() const; const T& getCurrent() const; /// Set base, modified and current to \a value. @@ -81,11 +91,16 @@ namespace MWMechanics /// Set base and adjust modified accordingly. void setBase (const T& value); - /// Set modified value an adjust base accordingly. + /// Set modified value and adjust base accordingly. void setModified (T value, const T& min, const T& max = std::numeric_limits::max()); - void setCurrent (const T& value, bool allowDecreaseBelowZero = false); - void setModifier (const T& modifier, bool allowCurrentDecreaseBelowZero=false); + /// Set "current modified," used for drain and fortify. Unlike the regular modifier + /// this just adds and subtracts from the current value without changing the maximum. + void setCurrentModified(T value); + + void setCurrent (const T& value, bool allowDecreaseBelowZero = false, bool allowIncreaseAboveModified = false); + void setModifier (const T& modifier, bool allowCurrentToDecreaseBelowZero=false); + void setCurrentModifier (const T& modifier, bool allowCurrentToDecreaseBelowZero = false); void writeState (ESM::StatState& state) const; void readState (const ESM::StatState& state); diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index fadc99b66..28e6f95dd 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -261,6 +261,7 @@ namespace MWScript .getDynamic (mIndex)); stat.setModified (diff + stat.getModified(), 0); + stat.setCurrentModified (diff + stat.getCurrentModified()); stat.setCurrent (diff + current); From fd89fa415a37c5680c3697135cdffe3ea1541675 Mon Sep 17 00:00:00 2001 From: Allofich Date: Sun, 3 Dec 2017 21:34:17 +0900 Subject: [PATCH 31/53] Do modifiers for dynamic stats before attributes (Fixes #4231) --- apps/openmw/mwmechanics/actors.cpp | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index e68a5d398..d53c4407b 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -697,6 +697,19 @@ namespace MWMechanics } } + // dynamic stats + for (int i = 0; i < 3; ++i) + { + DynamicStat stat = creatureStats.getDynamic(i); + stat.setCurrentModifier(effects.get(ESM::MagicEffect::FortifyHealth + i).getMagnitude() - + effects.get(ESM::MagicEffect::DrainHealth + i).getMagnitude(), + // Magicka can be decreased below zero due to a fortify effect wearing off + // Fatigue can be decreased below zero meaning the actor will be knocked out + i == 1 || i == 2); + + creatureStats.setDynamic(i, stat); + } + // attributes for(int i = 0;i < ESM::Attribute::Length;++i) { @@ -728,19 +741,6 @@ namespace MWMechanics } } - // dynamic stats - for(int i = 0;i < 3;++i) - { - DynamicStat stat = creatureStats.getDynamic(i); - stat.setCurrentModifier(effects.get(ESM::MagicEffect::FortifyHealth+i).getMagnitude() - - effects.get(ESM::MagicEffect::DrainHealth+i).getMagnitude(), - // Magicka can be decreased below zero due to a fortify effect wearing off - // Fatigue can be decreased below zero meaning the actor will be knocked out - i == 1 || i == 2); - - creatureStats.setDynamic(i, stat); - } - // AI setting modifiers int creature = !ptr.getClass().isNpc(); if (creature && ptr.get()->mBase->mData.mType == ESM::Creature::Humanoid) From 94d941cabc62d339b34da2487dfd87764416b044 Mon Sep 17 00:00:00 2001 From: Allofich <19624336+Allofich@users.noreply.github.com> Date: Mon, 13 Aug 2018 22:35:45 +0900 Subject: [PATCH 32/53] Update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bd51b8bb6..1afd16adf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ Bug #2862: [macOS] Can't quit launcher using Command-Q or OpenMW->Quit Bug #2872: Tab completion in console doesn't work with explicit reference Bug #2971: Compiler did not reject lines with naked expressions beginning with x.y + Bug #3049: Drain and Fortify effects are not properly applied on health, magicka and fatigue Bug #3249: Fixed revert function not updating views properly Bug #3374: Touch spells not hitting kwama foragers Bug #3486: [Mod] NPC Commands does not work @@ -33,6 +34,7 @@ Bug #4215: OpenMW shows book text after last EOL tag Bug #4221: Characters get stuck in V-shaped terrain Bug #4230: AiTravel package issues break some Tribunal quests + Bug #4231: Infected rats from the "Crimson Plague" quest rendered unconscious by change in Drain Fatigue functionality Bug #4251: Stationary NPCs do not return to their position after combat Bug #4274: Pre-0.43 death animations are not forward-compatible with 0.43+ Bug #4286: Scripted animations can be interrupted From 5e172ed831d797ab14d31689d6e9dcd872d663fb Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 14 Aug 2018 10:30:27 +0400 Subject: [PATCH 33/53] Move cerr initialization out of 'try' block --- components/debug/debugging.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/components/debug/debugging.cpp b/components/debug/debugging.cpp index bec97207a..470df47a7 100644 --- a/components/debug/debugging.cpp +++ b/components/debug/debugging.cpp @@ -55,6 +55,14 @@ int wrapApplication(int (*innerApplication)(int argc, char *argv[]), int argc, c std::streambuf* cout_rdbuf = std::cout.rdbuf (); std::streambuf* cerr_rdbuf = std::cerr.rdbuf (); +#if !(defined(_WIN32) && defined(_DEBUG)) + boost::iostreams::stream_buffer coutsb; + boost::iostreams::stream_buffer cerrsb; +#endif + + const std::string logName = Misc::StringUtils::lowerCase(appName) + ".log"; + boost::filesystem::ofstream logfile; + int ret = 0; try { @@ -67,12 +75,8 @@ int wrapApplication(int (*innerApplication)(int argc, char *argv[]), int argc, c std::cerr.rdbuf (&sb); #else // Redirect cout and cerr to the log file - const std::string logName = Misc::StringUtils::lowerCase(appName) + ".log"; - boost::filesystem::ofstream logfile; logfile.open (boost::filesystem::path(cfgMgr.getLogPath() / logName)); - boost::iostreams::stream_buffer coutsb; - boost::iostreams::stream_buffer cerrsb; std::ostream oldcout(cout_rdbuf); std::ostream oldcerr(cerr_rdbuf); coutsb.open (Debug::Tee(logfile, oldcout)); @@ -90,7 +94,7 @@ int wrapApplication(int (*innerApplication)(int argc, char *argv[]), int argc, c #endif SDL_ShowSimpleMessageBox(0, (appName + ": Fatal error").c_str(), e.what(), NULL); - Log(Debug::Error) << "ERROR: " << e.what(); + Log(Debug::Error) << "Error: " << e.what(); ret = 1; } From 9a5b0162931222900a20d455361c3007f9af53c4 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 14 Aug 2018 11:17:05 +0400 Subject: [PATCH 34/53] Move crashcatcher initialization to components --- apps/opencs/editor.cpp | 7 ------- apps/openmw/main.cpp | 1 - components/debug/debugging.cpp | 9 +++++++++ 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index 6ca10e0f6..73208b926 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -5,9 +5,6 @@ #include #include - -#include - #include #include @@ -27,10 +24,6 @@ CS::Editor::Editor (int argc, char **argv) mLock(), mMerge (mDocumentManager), mIpcServerName ("org.openmw.OpenCS"), mServer(NULL), mClientSocket(NULL) { - // install the crash handler as soon as possible. note that the log path - // does not depend on config being read. - crashCatcherInstall(argc, argv, (mCfgMgr.getLogPath() / "openmw-cs-crash.log").string()); - std::pair > config = readConfig(); setupDataFiles (config.first); diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index bbe1267b1..f9cf58544 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -1,7 +1,6 @@ #include #include -#include #include #include #include diff --git a/components/debug/debugging.cpp b/components/debug/debugging.cpp index 470df47a7..a4c59c221 100644 --- a/components/debug/debugging.cpp +++ b/components/debug/debugging.cpp @@ -1,5 +1,7 @@ #include "debugging.hpp" +#include + namespace Debug { std::streamsize DebugOutputBase::write(const char *str, std::streamsize size) @@ -61,12 +63,14 @@ int wrapApplication(int (*innerApplication)(int argc, char *argv[]), int argc, c #endif const std::string logName = Misc::StringUtils::lowerCase(appName) + ".log"; + const std::string crashLogName = Misc::StringUtils::lowerCase(appName) + "-crash.log"; boost::filesystem::ofstream logfile; int ret = 0; try { Files::ConfigurationManager cfgMgr; + #if defined(_WIN32) && defined(_DEBUG) // Redirect cout and cerr to VS debug output when running in debug mode boost::iostreams::stream_buffer sb; @@ -85,6 +89,11 @@ int wrapApplication(int (*innerApplication)(int argc, char *argv[]), int argc, c std::cout.rdbuf (&coutsb); std::cerr.rdbuf (&cerrsb); #endif + + // install the crash handler as soon as possible. note that the log path + // does not depend on config being read. + crashCatcherInstall(argc, argv, (cfgMgr.getLogPath() / crashLogName).string()); + ret = innerApplication(argc, argv); } catch (std::exception& e) From 7029ed0e8d45010e8d2934073bccfdba4ff3c0c3 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Tue, 14 Aug 2018 15:36:52 +0300 Subject: [PATCH 35/53] Refactor follower and enemy actor processing Make another exception for wander packages when finding allies (bug #4304) --- apps/openmw/mwmechanics/actors.cpp | 105 +++++++++++++++-------------- 1 file changed, 54 insertions(+), 51 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index d53c4407b..5c59efe30 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -1777,38 +1777,35 @@ namespace MWMechanics std::list Actors::getActorsSidingWith(const MWWorld::Ptr& actor) { std::list list; - for(PtrActorMap::iterator iter(mActors.begin());iter != mActors.end();++iter) + for(PtrActorMap::iterator iter = mActors.begin(); iter != mActors.end(); ++iter) { - const MWWorld::Class &cls = iter->first.getClass(); - const CreatureStats &stats = cls.getCreatureStats(iter->first); + const MWWorld::Ptr &iteratedActor = iter->first; + if (iteratedActor == getPlayer()) + continue; + + const CreatureStats &stats = iteratedActor.getClass().getCreatureStats(iteratedActor); if (stats.isDead()) continue; - // An actor counts as siding with this actor if Follow or Escort is the current AI package, or there are only Combat packages before the Follow/Escort package - for (std::list::const_iterator it = stats.getAiSequence().begin(); it != stats.getAiSequence().end(); ++it) - { - if ((*it)->sideWithTarget() && (*it)->getTarget() == actor) - { - list.push_back(iter->first); - break; - } - else if ((*it)->getTypeId() != MWMechanics::AiPackage::TypeIdCombat) - break; - } + // An actor counts as siding with this actor if Follow or Escort is the current AI package, or there are only Combat and Wander packages before the Follow/Escort package // Actors that are targeted by this actor's Follow or Escort packages also side with them - if (actor != getPlayer()) + for (auto package = stats.getAiSequence().begin(); package != stats.getAiSequence().end(); ++package) { - const CreatureStats &stats2 = actor.getClass().getCreatureStats(actor); - for (std::list::const_iterator it2 = stats2.getAiSequence().begin(); it2 != stats2.getAiSequence().end(); ++it2) + const MWWorld::Ptr &target = (*package)->getTarget(); + if ((*package)->sideWithTarget() && !target.isEmpty()) { - if ((*it2)->sideWithTarget() && !(*it2)->getTarget().isEmpty()) + if (iteratedActor == actor) { - list.push_back((*it2)->getTarget()); - break; + list.push_back(target); } - else if ((*it2)->getTypeId() != MWMechanics::AiPackage::TypeIdCombat) - break; + else if (target == actor) + { + list.push_back(iteratedActor); + } + break; } + else if ((*package)->getTypeId() != AiPackage::TypeIdCombat && (*package)->getTypeId() != AiPackage::TypeIdWander) + break; } } return list; @@ -1819,17 +1816,21 @@ namespace MWMechanics std::list list; for(PtrActorMap::iterator iter(mActors.begin());iter != mActors.end();++iter) { - const MWWorld::Class &cls = iter->first.getClass(); - CreatureStats &stats = cls.getCreatureStats(iter->first); + const MWWorld::Ptr &iteratedActor = iter->first; + if (iteratedActor == getPlayer()) + continue; + + const CreatureStats &stats = iteratedActor.getClass().getCreatureStats(iteratedActor); if (stats.isDead()) continue; - // An actor counts as following if AiFollow is the current AiPackage, or there are only Combat packages before the AiFollow package - for (std::list::const_iterator it = stats.getAiSequence().begin(); it != stats.getAiSequence().end(); ++it) + // An actor counts as following if AiFollow is the current AiPackage, + // or there are only Combat and Wander packages before the AiFollow package + for (auto package = stats.getAiSequence().begin(); package != stats.getAiSequence().end(); ++package) { - if ((*it)->followTargetThroughDoors() && (*it)->getTarget() == actor) - list.push_back(iter->first); - else if ((*it)->getTypeId() != MWMechanics::AiPackage::TypeIdCombat) + if ((*package)->followTargetThroughDoors() && (*package)->getTarget() == actor) + list.push_back(iteratedActor); + else if ((*package)->getTypeId() != AiPackage::TypeIdCombat && (*package)->getTypeId() != AiPackage::TypeIdWander) break; } } @@ -1878,24 +1879,24 @@ namespace MWMechanics std::list list; for(PtrActorMap::iterator iter(mActors.begin());iter != mActors.end();++iter) { - const MWWorld::Class &cls = iter->first.getClass(); - CreatureStats &stats = cls.getCreatureStats(iter->first); + const MWWorld::Ptr &iteratedActor = iter->first; + if (iteratedActor == getPlayer()) + continue; + + const CreatureStats &stats = iteratedActor.getClass().getCreatureStats(iteratedActor); if (stats.isDead()) continue; - // An actor counts as following if AiFollow is the current AiPackage, or there are only Combat packages before the AiFollow package - for (std::list::const_iterator it = stats.getAiSequence().begin(); it != stats.getAiSequence().end(); ++it) + // An actor counts as following if AiFollow is the current AiPackage, + // or there are only Combat and Wander packages before the AiFollow package + for (auto package = stats.getAiSequence().begin(); package != stats.getAiSequence().end(); ++package) { - if ((*it)->getTypeId() == MWMechanics::AiPackage::TypeIdFollow) + if ((*package)->followTargetThroughDoors() && (*package)->getTarget() == actor) { - MWWorld::Ptr followTarget = (*it)->getTarget(); - if (followTarget.isEmpty()) - continue; - if (followTarget == actor) - list.push_back(static_cast(*it)->getFollowIndex()); + list.push_back(static_cast(*package)->getFollowIndex()); break; } - else if ((*it)->getTypeId() != MWMechanics::AiPackage::TypeIdCombat) + else if ((*package)->getTypeId() != AiPackage::TypeIdCombat && (*package)->getTypeId() != AiPackage::TypeIdWander) break; } } @@ -1907,14 +1908,14 @@ namespace MWMechanics std::vector neighbors; osg::Vec3f position (actor.getRefData().getPosition().asVec3()); getObjectsInRange(position, aiProcessingDistance, neighbors); - for(std::vector::const_iterator iter(neighbors.begin());iter != neighbors.end();++iter) + for(auto neighbor = neighbors.begin(); neighbor != neighbors.end(); ++neighbor) { - const MWWorld::Class &cls = iter->getClass(); - const CreatureStats &stats = cls.getCreatureStats(*iter); - if (stats.isDead() || *iter == actor) + const CreatureStats &stats = neighbor->getClass().getCreatureStats(*neighbor); + if (stats.isDead() || *neighbor == actor) continue; + if (stats.getAiSequence().isInCombat(actor)) - list.push_front(*iter); + list.push_front(*neighbor); } return list; } @@ -1927,14 +1928,16 @@ namespace MWMechanics getObjectsInRange(position, aiProcessingDistance, neighbors); std::list followers = getActorsFollowing(actor); - for(std::vector::const_iterator iter(neighbors.begin());iter != neighbors.end();++iter) + for(auto neighbor = neighbors.begin(); neighbor != neighbors.end(); ++neighbor) { - const CreatureStats &stats = iter->getClass().getCreatureStats(*iter); - if (stats.isDead() || *iter == actor || iter->getClass().isPureWaterCreature(*iter)) + const CreatureStats &stats = neighbor->getClass().getCreatureStats(*neighbor); + if (stats.isDead() || *neighbor == actor || neighbor->getClass().isPureWaterCreature(*neighbor)) continue; - const bool isFollower = std::find(followers.begin(), followers.end(), *iter) != followers.end(); - if (stats.getAiSequence().isInCombat(actor) || (MWBase::Environment::get().getMechanicsManager()->isAggressive(*iter, actor) && !isFollower)) - list.push_back(*iter); + + const bool isFollower = std::find(followers.begin(), followers.end(), *neighbor) != followers.end(); + + if (stats.getAiSequence().isInCombat(actor) || (MWBase::Environment::get().getMechanicsManager()->isAggressive(*neighbor, actor) && !isFollower)) + list.push_back(*neighbor); } return list; } From 75bd6e1d2846ab1398b18778b47f392163fb6f2d Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Tue, 14 Aug 2018 15:41:34 +0300 Subject: [PATCH 36/53] Make search for allies in actorAttacked recursive --- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 7814b4a91..e9915397a 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -1,6 +1,7 @@ #include "mechanicsmanagerimp.hpp" #include +#include #include @@ -1445,11 +1446,12 @@ namespace MWMechanics if (target == getPlayer() || !attacker.getClass().isActor()) return false; - std::list followersAttacker = getActorsSidingWith(attacker); + std::set followersAttacker; + getActorsSidingWith(attacker, followersAttacker); MWMechanics::CreatureStats& statsTarget = target.getClass().getCreatureStats(target); - if (std::find(followersAttacker.begin(), followersAttacker.end(), target) != followersAttacker.end()) + if (followersAttacker.find(target) != followersAttacker.end()) { statsTarget.friendlyHit(); From 53599290c3ffd2c5f186f1e4cde52ad863959b79 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Tue, 14 Aug 2018 16:14:48 +0300 Subject: [PATCH 37/53] Make search for followers in getEnemiesNearby recursive --- apps/openmw/mwmechanics/actors.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 5c59efe30..956265402 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -2,7 +2,6 @@ #include #include - #include #include #include @@ -1927,7 +1926,8 @@ namespace MWMechanics osg::Vec3f position (actor.getRefData().getPosition().asVec3()); getObjectsInRange(position, aiProcessingDistance, neighbors); - std::list followers = getActorsFollowing(actor); + std::set followers; + getActorsFollowing(actor, followers); for(auto neighbor = neighbors.begin(); neighbor != neighbors.end(); ++neighbor) { const CreatureStats &stats = neighbor->getClass().getCreatureStats(*neighbor); From 22162dcbda39dc3ef568e6316a4c7fa73e1eb2a9 Mon Sep 17 00:00:00 2001 From: Capostrophic <21265616+Capostrophic@users.noreply.github.com> Date: Tue, 14 Aug 2018 18:14:43 +0300 Subject: [PATCH 38/53] Replace std::find with std::set::find where applicable --- apps/openmw/mwmechanics/actors.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 956265402..3b1f6f5a2 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -403,7 +403,7 @@ namespace MWMechanics std::set playerAllies; getActorsSidingWith(MWMechanics::getPlayer(), playerAllies, cachedAllies); - bool isPlayerFollowerOrEscorter = std::find(playerAllies.begin(), playerAllies.end(), actor1) != playerAllies.end(); + bool isPlayerFollowerOrEscorter = playerAllies.find(actor1) != playerAllies.end(); // If actor2 and at least one actor2 are in combat with actor1, actor1 and its allies start combat with them // Doesn't apply for player followers/escorters @@ -457,7 +457,7 @@ namespace MWMechanics // Do aggression check if actor2 is the player or a player follower or escorter if (!aggressive) { - if (againstPlayer || std::find(playerAllies.begin(), playerAllies.end(), actor2) != playerAllies.end()) + if (againstPlayer || playerAllies.find(actor2) != playerAllies.end()) { // Player followers and escorters with high fight should not initiate combat with the player or with // other player followers or escorters @@ -1928,13 +1928,13 @@ namespace MWMechanics std::set followers; getActorsFollowing(actor, followers); - for(auto neighbor = neighbors.begin(); neighbor != neighbors.end(); ++neighbor) + for (auto neighbor = neighbors.begin(); neighbor != neighbors.end(); ++neighbor) { const CreatureStats &stats = neighbor->getClass().getCreatureStats(*neighbor); if (stats.isDead() || *neighbor == actor || neighbor->getClass().isPureWaterCreature(*neighbor)) continue; - const bool isFollower = std::find(followers.begin(), followers.end(), *neighbor) != followers.end(); + const bool isFollower = followers.find(*neighbor) != followers.end(); if (stats.getAiSequence().isInCombat(actor) || (MWBase::Environment::get().getMechanicsManager()->isAggressive(*neighbor, actor) && !isFollower)) list.push_back(*neighbor); From 52f70642d0f3e88573aa06e5e74c4a23d52ba91f Mon Sep 17 00:00:00 2001 From: Capostrophic <21265616+Capostrophic@users.noreply.github.com> Date: Tue, 14 Aug 2018 18:25:19 +0300 Subject: [PATCH 39/53] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f1f80aa84..47b277f89 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,7 @@ Bug #4286: Scripted animations can be interrupted Bug #4291: Non-persistent actors that started the game as dead do not play death animations Bug #4293: Faction members are not aware of faction ownerships in barter + Bug #4304: "Follow" not working as a second AI package Bug #4307: World cleanup should remove dead bodies only if death animation is finished Bug #4311: OpenMW does not handle RootCollisionNode correctly Bug #4327: Missing animations during spell/weapon stance switching From 1452684d9e58c8e59c3d2bf85c837894b2feb6fe Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 14 Aug 2018 19:42:41 +0400 Subject: [PATCH 40/53] Use new logging system for components --- components/crashcatcher/crashcatcher.cpp | 11 +++-- components/debug/debuglog.hpp | 1 - components/esm/cellref.cpp | 6 +-- components/esm/loadcrea.cpp | 4 +- components/esm/loaddial.cpp | 4 +- components/esm/loadscpt.cpp | 12 ++--- components/esmterrain/storage.cpp | 3 +- components/files/configurationmanager.cpp | 9 ++-- components/files/multidircollection.cpp | 6 +-- components/fontloader/fontloader.cpp | 6 ++- components/interpreter/defines.cpp | 6 +-- components/myguiplatform/myguidatamanager.cpp | 4 +- components/myguiplatform/myguitexture.cpp | 3 +- components/nif/niffile.hpp | 5 +- components/nifbullet/bulletnifloader.cpp | 5 +- components/nifbullet/bulletnifloader.hpp | 6 +-- components/nifosg/nifloader.cpp | 48 +++++++++---------- components/nifosg/particle.cpp | 3 +- components/resource/imagemanager.cpp | 9 ++-- components/resource/scenemanager.cpp | 11 +++-- components/sceneutil/attach.cpp | 3 +- components/sceneutil/riggeometry.cpp | 11 +++-- components/sceneutil/skeleton.cpp | 5 +- components/sceneutil/unrefqueue.cpp | 4 +- components/sceneutil/workqueue.cpp | 4 +- components/sdlutil/sdlcursormanager.cpp | 8 ++-- components/sdlutil/sdlinputwrapper.cpp | 6 +-- components/sdlutil/sdlvideowrapper.cpp | 4 +- components/settings/settings.cpp | 26 +++++----- components/shader/shadermanager.cpp | 16 ++++--- components/shader/shadervisitor.cpp | 3 +- components/to_utf8/to_utf8.cpp | 7 ++- components/vfs/filesystemarchive.cpp | 6 +-- components/vfs/registerarchives.cpp | 9 ++-- components/widgets/imagebutton.cpp | 4 +- 35 files changed, 144 insertions(+), 134 deletions(-) diff --git a/components/crashcatcher/crashcatcher.cpp b/components/crashcatcher/crashcatcher.cpp index f7b8717a6..04b239a7f 100644 --- a/components/crashcatcher/crashcatcher.cpp +++ b/components/crashcatcher/crashcatcher.cpp @@ -14,7 +14,7 @@ #include #include -#include +#include #include @@ -185,7 +185,7 @@ static void gdb_info(pid_t pid) if (close(fd) == 0) remove(respfile); else - std::cerr << "Warning: can not close and remove file '" << respfile << "': " << std::strerror(errno) << std::endl; + Log(Debug::Warning) << "Warning: can not close and remove file '" << respfile << "': " << std::strerror(errno); } printf("!!! Could not create gdb command file\n"); } @@ -517,8 +517,9 @@ void crashCatcherInstall(int argc, char **argv, const std::string &crashLogPath) int s[5] = { SIGSEGV, SIGILL, SIGFPE, SIGBUS, SIGABRT }; if (crashCatcherInstallHandlers(argc, argv, 5, s, crashLogPath.c_str(), NULL) == -1) { - std::cerr << "Installing crash handler failed" << std::endl; - } else - std::cout << "Crash handler installed" << std::endl; + Log(Debug::Warning) << "Installing crash handler failed"; + } + else + Log(Debug::Info) << "Crash handler installed"; } } diff --git a/components/debug/debuglog.hpp b/components/debug/debuglog.hpp index bfd7d1196..1ea18aa9b 100644 --- a/components/debug/debuglog.hpp +++ b/components/debug/debuglog.hpp @@ -2,7 +2,6 @@ #define DEBUG_LOG_H #include -#include #include namespace Debug diff --git a/components/esm/cellref.cpp b/components/esm/cellref.cpp index 1f7c2b964..4b9c6b073 100644 --- a/components/esm/cellref.cpp +++ b/components/esm/cellref.cpp @@ -1,6 +1,6 @@ #include "cellref.hpp" -#include +#include #include "esmreader.hpp" #include "esmwriter.hpp" @@ -48,9 +48,7 @@ void ESM::CellRef::loadId (ESMReader& esm, bool wideRefNum) mRefID = esm.getHNOString ("NAME"); if (mRefID.empty()) { - std::ios::fmtflags f(std::cerr.flags()); - std::cerr << "Warning: got CellRef with empty RefId in " << esm.getName() << " 0x" << std::hex << esm.getFileOffset() << std::endl; - std::cerr.flags(f); + Log(Debug::Warning) << "Warning: got CellRef with empty RefId in " << esm.getName() << " 0x" << std::hex << esm.getFileOffset(); } } diff --git a/components/esm/loadcrea.cpp b/components/esm/loadcrea.cpp index ddd49f8df..83315a5b8 100644 --- a/components/esm/loadcrea.cpp +++ b/components/esm/loadcrea.cpp @@ -1,6 +1,6 @@ #include "loadcrea.hpp" -#include +#include #include "esmreader.hpp" #include "esmwriter.hpp" @@ -89,7 +89,7 @@ namespace ESM { // seems to occur only in .ESS files, unsure of purpose int index; esm.getHT(index); - std::cerr << "Creature::load: Unhandled INDX " << index << std::endl; + Log(Debug::Warning) << "Creature::load: Unhandled INDX " << index; break; default: esm.fail("Unknown subrecord"); diff --git a/components/esm/loaddial.cpp b/components/esm/loaddial.cpp index 9ef3d3964..d7e0a6ee1 100644 --- a/components/esm/loaddial.cpp +++ b/components/esm/loaddial.cpp @@ -1,6 +1,6 @@ #include "loaddial.hpp" -#include +#include #include "esmreader.hpp" #include "esmwriter.hpp" @@ -128,7 +128,7 @@ namespace ESM return; } - std::cerr << "Warning: Failed to insert info " << info.mId << std::endl; + Log(Debug::Warning) << "Warning: Failed to insert info " << info.mId; } void Dialogue::clearDeletedInfos() diff --git a/components/esm/loadscpt.cpp b/components/esm/loadscpt.cpp index 19246c4c4..474c2b423 100644 --- a/components/esm/loadscpt.cpp +++ b/components/esm/loadscpt.cpp @@ -1,11 +1,11 @@ #include "loadscpt.hpp" +#include + #include "esmreader.hpp" #include "esmwriter.hpp" #include "defs.hpp" -#include - namespace ESM { unsigned int Script::sRecordId = REC_SCPT; @@ -45,12 +45,12 @@ namespace ESM // an exeption, just log an error and continue. std::stringstream ss; - ss << "ESM Error: " << "String table overflow"; + ss << "String table overflow"; ss << "\n File: " << esm.getName(); ss << "\n Record: " << esm.getContext().recName.toString(); ss << "\n Subrecord: " << "SCVR"; ss << "\n Offset: 0x" << std::hex << esm.getFileOffset(); - std::cerr << ss.str() << std::endl; + Log(Debug::Verbose) << ss.str(); break; } @@ -91,10 +91,10 @@ namespace ESM if (subSize != static_cast(mData.mScriptDataSize)) { std::stringstream ss; - ss << "ESM Warning: Script data size defined in SCHD subrecord does not match size of SCDT subrecord"; + ss << "Script data size defined in SCHD subrecord does not match size of SCDT subrecord"; ss << "\n File: " << esm.getName(); ss << "\n Offset: 0x" << std::hex << esm.getFileOffset(); - std::cerr << ss.str() << std::endl; + Log(Debug::Verbose) << ss.str(); } mScriptData.resize(subSize); diff --git a/components/esmterrain/storage.cpp b/components/esmterrain/storage.cpp index 0f5b17502..f77e66276 100644 --- a/components/esmterrain/storage.cpp +++ b/components/esmterrain/storage.cpp @@ -10,6 +10,7 @@ #include +#include #include #include @@ -378,7 +379,7 @@ namespace ESMTerrain const ESM::LandTexture* ltex = getLandTexture(id.first-1, id.second); if (!ltex) { - std::cerr << "Warning: Unable to find land texture index " << id.first-1 << " in plugin " << id.second << ", using default texture instead" << std::endl; + Log(Debug::Warning) << "Warning: Unable to find land texture index " << id.first-1 << " in plugin " << id.second << ", using default texture instead"; return defaultTexture; } diff --git a/components/files/configurationmanager.cpp b/components/files/configurationmanager.cpp index 7c3956a29..333ec5ced 100644 --- a/components/files/configurationmanager.cpp +++ b/components/files/configurationmanager.cpp @@ -1,7 +1,6 @@ #include "configurationmanager.hpp" -#include - +#include #include #include @@ -134,7 +133,7 @@ bool ConfigurationManager::loadConfig(const boost::filesystem::path& path, if (boost::filesystem::is_regular_file(cfgFile)) { if (!mSilent) - std::cout << "Loading config file: " << cfgFile.string() << "... "; + Log(Debug::Info) << "Loading config file: " << cfgFile.string() << "... "; boost::filesystem::ifstream configFileStreamUnfiltered(cfgFile); boost::iostreams::filtering_istream configFileStream; @@ -146,13 +145,13 @@ bool ConfigurationManager::loadConfig(const boost::filesystem::path& path, configFileStream, description, true), variables); if (!mSilent) - std::cout << "done." << std::endl; + Log(Debug::Info) << "done."; return true; } else { if (!mSilent) - std::cout << "failed." << std::endl; + Log(Debug::Info) << "failed."; return false; } } diff --git a/components/files/multidircollection.cpp b/components/files/multidircollection.cpp index 93db6834d..98e25fcc8 100644 --- a/components/files/multidircollection.cpp +++ b/components/files/multidircollection.cpp @@ -1,9 +1,9 @@ #include "multidircollection.hpp" -#include - #include +#include + namespace Files { struct NameEqual @@ -46,7 +46,7 @@ namespace Files { if (!boost::filesystem::is_directory(*iter)) { - std::cout << "Skipping invalid directory: " << (*iter).string() << std::endl; + Log(Debug::Info) << "Skipping invalid directory: " << (*iter).string(); continue; } diff --git a/components/fontloader/fontloader.cpp b/components/fontloader/fontloader.cpp index 080739bc1..790df7fa8 100644 --- a/components/fontloader/fontloader.cpp +++ b/components/fontloader/fontloader.cpp @@ -14,6 +14,8 @@ #include #include +#include + #include #include @@ -281,7 +283,7 @@ namespace Gui assert (image->isDataContiguous()); memcpy(image->data(), &textureData[0], textureData.size()); - std::cout << "Writing " << resourceName + ".png" << std::endl; + Log(Debug::Info) << "Writing " << resourceName + ".png"; osgDB::writeImageFile(*image, resourceName + ".png"); } @@ -461,7 +463,7 @@ namespace Gui if (exportToFile) { - std::cout << "Writing " << resourceName + ".xml" << std::endl; + Log(Debug::Info) << "Writing " << resourceName + ".xml"; xmlDocument.createDeclaration(); xmlDocument.save(resourceName + ".xml"); } diff --git a/components/interpreter/defines.cpp b/components/interpreter/defines.cpp index 515c6c7d5..3c6226d9c 100644 --- a/components/interpreter/defines.cpp +++ b/components/interpreter/defines.cpp @@ -3,8 +3,8 @@ #include #include #include -#include +#include #include namespace Interpreter{ @@ -191,8 +191,8 @@ namespace Interpreter{ } catch (std::exception& e) { - std::cerr << "Error: Failed to replace escape character, with the following error: " << e.what() << std::endl; - std::cerr << "Full text below: " << std::endl << text << std::endl; + Log(Debug::Error) << "Error: Failed to replace escape character, with the following error: " << e.what(); + Log(Debug::Error) << "Full text below:\n" << text; } // Not found, or error diff --git a/components/myguiplatform/myguidatamanager.cpp b/components/myguiplatform/myguidatamanager.cpp index 485a87ba7..ddd7ca342 100644 --- a/components/myguiplatform/myguidatamanager.cpp +++ b/components/myguiplatform/myguidatamanager.cpp @@ -5,7 +5,7 @@ #include #include -#include +#include namespace osgMyGUI { @@ -23,7 +23,7 @@ MyGUI::IDataStream *DataManager::getData(const std::string &name) stream->open(fullpath, std::ios::binary); if (stream->fail()) { - std::cerr << "DataManager::getData: Failed to open '" << name << "'" << std::endl; + Log(Debug::Error) << "DataManager::getData: Failed to open '" << name << "'"; return NULL; } return new MyGUI::DataFileStream(stream.release()); diff --git a/components/myguiplatform/myguitexture.cpp b/components/myguiplatform/myguitexture.cpp index a3b70eac9..6c53a9699 100644 --- a/components/myguiplatform/myguitexture.cpp +++ b/components/myguiplatform/myguitexture.cpp @@ -5,6 +5,7 @@ #include +#include #include namespace osgMyGUI @@ -112,7 +113,7 @@ namespace osgMyGUI void OSGTexture::saveToFile(const std::string &fname) { - std::cerr << "Would save image to file " << fname << std::endl; + Log(Debug::Warning) << "Would save image to file " << fname; } int OSGTexture::getWidth() diff --git a/components/nif/niffile.hpp b/components/nif/niffile.hpp index cab2e9880..0893db72f 100644 --- a/components/nif/niffile.hpp +++ b/components/nif/niffile.hpp @@ -5,8 +5,8 @@ #include #include -#include +#include #include #include "record.hpp" @@ -80,8 +80,7 @@ public: /// Used when something goes wrong, but not catastrophically so void warn(const std::string &msg) const override { - std::cerr << " NIFFile Warning: " << msg < #include +#include + #include #include @@ -18,7 +20,6 @@ #include #include - namespace { @@ -203,7 +204,7 @@ void BulletNifLoader::handleNode(const std::string& fileName, const Nif::Node *n isCollisionNode = isCollisionNode || (node->recType == Nif::RC_RootCollisionNode); if (node->recType == Nif::RC_RootCollisionNode && autogenerated) - std::cerr << "Found RootCollisionNode attached to non-root node in " << fileName << ". Treat it as a common NiTriShape." << std::endl; + Log(Debug::Info) << "Found RootCollisionNode attached to non-root node in " << fileName << ". Treat it as a common NiTriShape."; // Don't collide with AvoidNode shapes if(node->recType == Nif::RC_AvoidNode) diff --git a/components/nifbullet/bulletnifloader.hpp b/components/nifbullet/bulletnifloader.hpp index 44134fd23..f970b7f3e 100644 --- a/components/nifbullet/bulletnifloader.hpp +++ b/components/nifbullet/bulletnifloader.hpp @@ -4,7 +4,6 @@ #include #include #include -#include #include #include @@ -12,6 +11,7 @@ #include #include +#include #include #include @@ -41,12 +41,12 @@ public: void warn(const std::string &msg) { - std::cerr << "NIFLoader: Warn:" << msg << "\n"; + Log(Debug::Warning) << "NIFLoader: Warn:" << msg; } void fail(const std::string &msg) { - std::cerr << "NIFLoader: Fail: "<< msg << std::endl; + Log(Debug::Error) << "NIFLoader: Fail: "<< msg; abort(); } diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 5857a3987..3198e995c 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -243,7 +243,7 @@ namespace NifOsg callback->setFunction(std::shared_ptr(new NifOsg::ControllerFunction(key))); if (target.mKeyframeControllers.find(strdata->string) != target.mKeyframeControllers.end()) - std::cerr << "Warning: controller " << strdata->string << " present more than once in " << nif->getFilename() << ", ignoring later version" << std::endl; + Log(Debug::Verbose) << "Controller " << strdata->string << " present more than once in " << nif->getFilename() << ", ignoring later version"; else target.mKeyframeControllers[strdata->string] = callback; } @@ -357,20 +357,20 @@ namespace NifOsg { if (nifNode->recType != Nif::RC_NiTextureEffect) { - std::cerr << "Unhandled effect " << nifNode->recName << " in " << mFilename << std::endl; + Log(Debug::Info) << "Unhandled effect " << nifNode->recName << " in " << mFilename; return; } const Nif::NiTextureEffect* textureEffect = static_cast(nifNode); if (textureEffect->textureType != Nif::NiTextureEffect::Environment_Map) { - std::cerr << "Unhandled NiTextureEffect type " << textureEffect->textureType << " in " << mFilename << std::endl; + Log(Debug::Info) << "Unhandled NiTextureEffect type " << textureEffect->textureType << " in " << mFilename; return; } if (textureEffect->texture.empty()) { - std::cerr << "NiTextureEffect missing source texture in " << mFilename << std::endl; + Log(Debug::Info) << "NiTextureEffect missing source texture in " << mFilename; return; } @@ -387,7 +387,7 @@ namespace NifOsg texGen->setMode(osg::TexGen::SPHERE_MAP); break; default: - std::cerr << "Unhandled NiTextureEffect coordGenType " << textureEffect->coordGenType << " in " << mFilename << std::endl; + Log(Debug::Info) << "Unhandled NiTextureEffect coordGenType " << textureEffect->coordGenType << " in " << mFilename; return; } @@ -638,7 +638,7 @@ namespace NifOsg else if(ctrl->recType == Nif::RC_NiGeomMorpherController) {} // handled in handleTriShape else - std::cerr << "Unhandled controller " << ctrl->recName << " on node " << nifNode->recIndex << " in " << mFilename << std::endl; + Log(Debug::Info) << "Unhandled controller " << ctrl->recName << " on node " << nifNode->recIndex << " in " << mFilename; } } @@ -664,7 +664,7 @@ namespace NifOsg handleVisController(static_cast(ctrl.getPtr()), transformNode, animflags); } else - std::cerr << "Unhandled controller " << ctrl->recName << " on node " << nifNode->recIndex << " in " << mFilename << std::endl; + Log(Debug::Info) << "Unhandled controller " << ctrl->recName << " on node " << nifNode->recIndex << " in " << mFilename; } } @@ -696,7 +696,7 @@ namespace NifOsg composite->addController(osgctrl); } else - std::cerr << "Unexpected material controller " << ctrl->recType << " in " << mFilename << std::endl; + Log(Debug::Info) << "Unexpected material controller " << ctrl->recType << " in " << mFilename; } } @@ -736,7 +736,7 @@ namespace NifOsg composite->addController(callback); } else - std::cerr << "Unexpected texture controller " << ctrl->recName << " in " << mFilename << std::endl; + Log(Debug::Info) << "Unexpected texture controller " << ctrl->recName << " in " << mFilename; } } @@ -769,7 +769,7 @@ namespace NifOsg // unused } else - std::cerr << "Unhandled particle modifier " << affectors->recName << " in " << mFilename << std::endl; + Log(Debug::Info) << "Unhandled particle modifier " << affectors->recName << " in " << mFilename; } for (; !colliders.empty(); colliders = colliders->extra) { @@ -784,7 +784,7 @@ namespace NifOsg program->addOperator(new SphericalCollider(sphericalcollider)); } else - std::cerr << "Unhandled particle collider " << colliders->recName << " in " << mFilename << std::endl; + Log(Debug::Info) << "Unhandled particle collider " << colliders->recName << " in " << mFilename; } } @@ -881,11 +881,11 @@ namespace NifOsg if(ctrl->recType == Nif::RC_NiParticleSystemController || ctrl->recType == Nif::RC_NiBSPArrayController) partctrl = static_cast(ctrl.getPtr()); else - std::cerr << "Unhandled controller " << ctrl->recName << " on node " << nifNode->recIndex << " in " << mFilename << std::endl; + Log(Debug::Info) << "Unhandled controller " << ctrl->recName << " on node " << nifNode->recIndex << " in " << mFilename; } if (!partctrl) { - std::cerr << "No particle controller found in " << mFilename << std::endl; + Log(Debug::Info) << "No particle controller found in " << mFilename; return; } @@ -925,7 +925,7 @@ namespace NifOsg rootNode->accept(find); if (!find.mFound) { - std::cerr << "can't find emitter node, wrong node order? in " << mFilename << std::endl; + Log(Debug::Info) << "can't find emitter node, wrong node order? in " << mFilename; return; } osg::Group* emitterNode = find.mFound; @@ -1121,7 +1121,7 @@ namespace NifOsg case 9: return osg::BlendFunc::ONE_MINUS_DST_ALPHA; case 10: return osg::BlendFunc::SRC_ALPHA_SATURATE; default: - std::cerr<< "Unexpected blend mode: "<< mode << " in " << mFilename << std::endl; + Log(Debug::Info) << "Unexpected blend mode: "<< mode << " in " << mFilename; return osg::BlendFunc::SRC_ALPHA; } } @@ -1139,7 +1139,7 @@ namespace NifOsg case 6: return osg::AlphaFunc::GEQUAL; case 7: return osg::AlphaFunc::NEVER; default: - std::cerr << "Unexpected blend mode: " << mode << " in " << mFilename << std::endl; + Log(Debug::Info) << "Unexpected blend mode: " << mode << " in " << mFilename; return osg::AlphaFunc::LEQUAL; } } @@ -1157,7 +1157,7 @@ namespace NifOsg case 6: return osg::Stencil::GEQUAL; case 7: return osg::Stencil::NEVER; // NifSkope says this is GL_ALWAYS, but in MW it's GL_NEVER default: - std::cerr << "Unexpected stencil function: " << func << " in " << mFilename << std::endl; + Log(Debug::Info) << "Unexpected stencil function: " << func << " in " << mFilename; return osg::Stencil::NEVER; } } @@ -1173,7 +1173,7 @@ namespace NifOsg case 4: return osg::Stencil::DECR; case 5: return osg::Stencil::INVERT; default: - std::cerr << "Unexpected stencil operation: " << op << " in " << mFilename << std::endl; + Log(Debug::Info) << "Unexpected stencil operation: " << op << " in " << mFilename; return osg::Stencil::KEEP; } } @@ -1192,7 +1192,7 @@ namespace NifOsg pixelformat = GL_RGBA; break; default: - std::cerr << "Unhandled internal pixel format " << pixelData->fmt << " in " << mFilename << std::endl; + Log(Debug::Info) << "Unhandled internal pixel format " << pixelData->fmt << " in " << mFilename; return NULL; } @@ -1210,7 +1210,7 @@ namespace NifOsg size_t mipSize = mip.height * mip.width * pixelData->bpp / 8; if (mipSize + mip.dataOffset > pixelData->data.size()) { - std::cerr << "Warning: Internal texture's mipmap data out of bounds, ignoring texture" << std::endl; + Log(Debug::Info) << "Internal texture's mipmap data out of bounds, ignoring texture"; return NULL; } @@ -1225,7 +1225,7 @@ namespace NifOsg if (width <= 0 || height <= 0) { - std::cerr << "Warning: Internal Texture Width and height must be non zero, ignoring texture" << std::endl; + Log(Debug::Info) << "Internal Texture Width and height must be non zero, ignoring texture"; return NULL; } @@ -1267,12 +1267,12 @@ namespace NifOsg { // Not used by the vanilla engine. MCP (Morrowind Code Patch) adds an option to use Gloss maps: // "- Gloss map fix. Morrowind removed gloss map entries from model files after loading them. This stops Morrowind from removing them." - //std::cerr << "NiTexturingProperty::GlossTexture in " << mFilename << " not currently used." << std::endl; + // Log(Debug::Info) << "NiTexturingProperty::GlossTexture in " << mFilename << " not currently used."; continue; } default: { - std::cerr << "Warning: unhandled texture stage " << i << " in " << mFilename << std::endl; + Log(Debug::Info) << "Unhandled texture stage " << i << " in " << mFilename; continue; } } @@ -1478,7 +1478,7 @@ namespace NifOsg break; } default: - std::cerr << "Unhandled " << property->recName << " in " << mFilename << std::endl; + Log(Debug::Info) << "Unhandled " << property->recName << " in " << mFilename; break; } } diff --git a/components/nifosg/particle.cpp b/components/nifosg/particle.cpp index 62360b9d6..03e836a0f 100644 --- a/components/nifosg/particle.cpp +++ b/components/nifosg/particle.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -284,7 +285,7 @@ void Emitter::emitParticles(double dt) if (!visitor.mFound) { - std::cerr << "Error: Can't find emitter node" << randomRecIndex << std::endl; + Log(Debug::Info) << "Can't find emitter node" << randomRecIndex; return; } diff --git a/components/resource/imagemanager.cpp b/components/resource/imagemanager.cpp index 000a833cf..c1d71ee00 100644 --- a/components/resource/imagemanager.cpp +++ b/components/resource/imagemanager.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include "objectcache.hpp" @@ -97,7 +98,7 @@ namespace Resource } catch (std::exception& e) { - std::cerr << "Failed to open image: " << e.what() << std::endl; + Log(Debug::Error) << "Failed to open image: " << e.what(); mCache->addEntryToObjectCache(normalized, mWarningImage); return mWarningImage; } @@ -109,7 +110,7 @@ namespace Resource osgDB::ReaderWriter* reader = osgDB::Registry::instance()->getReaderWriterForExtension(ext); if (!reader) { - std::cerr << "Error loading " << filename << ": no readerwriter for '" << ext << "' found" << std::endl; + Log(Debug::Error) << "Error loading " << filename << ": no readerwriter for '" << ext << "' found"; mCache->addEntryToObjectCache(normalized, mWarningImage); return mWarningImage; } @@ -117,7 +118,7 @@ namespace Resource osgDB::ReaderWriter::ReadResult result = reader->readImage(*stream, mOptions); if (!result.success()) { - std::cerr << "Error loading " << filename << ": " << result.message() << " code " << result.status() << std::endl; + Log(Debug::Error) << "Error loading " << filename << ": " << result.message() << " code " << result.status(); mCache->addEntryToObjectCache(normalized, mWarningImage); return mWarningImage; } @@ -130,7 +131,7 @@ namespace Resource static bool uncompress = (getenv("OPENMW_DECOMPRESS_TEXTURES") != 0); if (!uncompress) { - std::cerr << "Error loading " << filename << ": no S3TC texture compression support installed" << std::endl; + Log(Debug::Error) << "Error loading " << filename << ": no S3TC texture compression support installed"; mCache->addEntryToObjectCache(normalized, mWarningImage); return mWarningImage; } diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp index db8d99ab4..8edb3d765 100644 --- a/components/resource/scenemanager.cpp +++ b/components/resource/scenemanager.cpp @@ -1,6 +1,5 @@ #include "scenemanager.hpp" -#include #include #include @@ -13,6 +12,8 @@ #include #include +#include + #include #include @@ -501,7 +502,7 @@ namespace Resource normalized = "meshes/marker_error." + std::string(sMeshTypes[i]); if (mVFS->exists(normalized)) { - std::cerr << "Failed to load '" << name << "': " << e.what() << ", using marker_error." << sMeshTypes[i] << " instead" << std::endl; + Log(Debug::Error) << "Failed to load '" << name << "': " << e.what() << ", using marker_error." << sMeshTypes[i] << " instead"; Files::IStreamPtr file = mVFS->get(normalized); loaded = load(file, normalized, mImageManager, mNifFileManager); break; @@ -658,12 +659,12 @@ namespace Resource if(magfilter == "nearest") mag = osg::Texture::NEAREST; else if(magfilter != "linear") - std::cerr<< "Warning: Invalid texture mag filter: "< #include +#include #include #include @@ -63,7 +64,7 @@ namespace SceneUtil { osg::ref_ptr node = *it; if (node->getNumParents() > 1) - std::cerr << "Error CopyRigVisitor: node has multiple parents" << std::endl; + Log(Debug::Error) << "Error CopyRigVisitor: node has multiple parents"; while (node->getNumParents()) node->getParent(0)->removeChild(node); diff --git a/components/sceneutil/riggeometry.cpp b/components/sceneutil/riggeometry.cpp index 7f148cf5e..ee30f1c85 100644 --- a/components/sceneutil/riggeometry.cpp +++ b/components/sceneutil/riggeometry.cpp @@ -1,9 +1,10 @@ #include "riggeometry.hpp" #include -#include #include +#include + #include "skeleton.hpp" #include "util.hpp" @@ -96,13 +97,13 @@ bool RigGeometry::initFromParentSkeleton(osg::NodeVisitor* nv) if (!mSkeleton) { - std::cerr << "Error: A RigGeometry did not find its parent skeleton" << std::endl; + Log(Debug::Error) << "Error: A RigGeometry did not find its parent skeleton"; return false; } if (!mInfluenceMap) { - std::cerr << "Error: No InfluenceMap set on RigGeometry" << std::endl; + Log(Debug::Error) << "Error: No InfluenceMap set on RigGeometry"; return false; } @@ -113,7 +114,7 @@ bool RigGeometry::initFromParentSkeleton(osg::NodeVisitor* nv) Bone* bone = mSkeleton->getBone(it->first); if (!bone) { - std::cerr << "Error: RigGeometry did not find bone " << it->first << std::endl; + Log(Debug::Error) << "Error: RigGeometry did not find bone " << it->first ; continue; } @@ -166,7 +167,7 @@ void RigGeometry::cull(osg::NodeVisitor* nv) { if (!mSkeleton) { - std::cerr << "Error: RigGeometry rendering with no skeleton, should have been initialized by UpdateVisitor" << std::endl; + Log(Debug::Error) << "Error: RigGeometry rendering with no skeleton, should have been initialized by UpdateVisitor"; // try to recover anyway, though rendering is likely to be incorrect. if (!initFromParentSkeleton(nv)) return; diff --git a/components/sceneutil/skeleton.cpp b/components/sceneutil/skeleton.cpp index 94ae1f234..58ed9a27c 100644 --- a/components/sceneutil/skeleton.cpp +++ b/components/sceneutil/skeleton.cpp @@ -3,10 +3,9 @@ #include #include +#include #include -#include - namespace SceneUtil { @@ -183,7 +182,7 @@ void Bone::update(const osg::Matrixf* parentMatrixInSkeletonSpace) { if (!mNode) { - std::cerr << "Error: Bone without node " << std::endl; + Log(Debug::Error) << "Error: Bone without node"; return; } if (parentMatrixInSkeletonSpace) diff --git a/components/sceneutil/unrefqueue.cpp b/components/sceneutil/unrefqueue.cpp index 7e5646ecb..42ea31133 100644 --- a/components/sceneutil/unrefqueue.cpp +++ b/components/sceneutil/unrefqueue.cpp @@ -3,8 +3,8 @@ #include //#include -//#include +//#include #include namespace SceneUtil @@ -20,7 +20,7 @@ namespace SceneUtil //osg::Timer timer; //size_t objcount = mObjects.size(); mObjects.clear(); - //std::cout << "cleared " << objcount << " objects in " << timer.time_m() << std::endl; + //Log(Debug::Verbose) << "cleared " << objcount << " objects in " << timer.time_m(); } }; diff --git a/components/sceneutil/workqueue.cpp b/components/sceneutil/workqueue.cpp index 2cd1ec806..37e8563e1 100644 --- a/components/sceneutil/workqueue.cpp +++ b/components/sceneutil/workqueue.cpp @@ -1,6 +1,6 @@ #include "workqueue.hpp" -#include +#include namespace SceneUtil { @@ -71,7 +71,7 @@ void WorkQueue::addWorkItem(osg::ref_ptr item, bool front) { if (item->isDone()) { - std::cerr << "Error: trying to add a work item that is already completed" << std::endl; + Log(Debug::Error) << "Error: trying to add a work item that is already completed"; return; } diff --git a/components/sdlutil/sdlcursormanager.cpp b/components/sdlutil/sdlcursormanager.cpp index 1747c9b94..1560b74b3 100644 --- a/components/sdlutil/sdlcursormanager.cpp +++ b/components/sdlutil/sdlcursormanager.cpp @@ -17,6 +17,8 @@ #include #include +#include + #include "imagetosurface.hpp" #if defined(OSG_LIBRARY_STATIC) && !defined(ANDROID) @@ -63,7 +65,7 @@ namespace CursorDecompression if (!_gc) { - std::cerr << "Failed to create pbuffer, failing back to normal graphics window." << std::endl; + Log(Debug::Warning) << "Failed to create pbuffer, failing back to normal graphics window."; traits->pbuffer = false; _gc = osg::GraphicsContext::createGraphicsContext(traits.get()); @@ -283,8 +285,8 @@ namespace SDLUtil mCursorMap.insert(CursorMap::value_type(std::string(name), curs)); } catch (std::exception& e) { - std::cerr << e.what() << std::endl; - std::cerr <<"Using default cursor."< +#include #include @@ -160,9 +160,7 @@ InputWrapper::InputWrapper(SDL_Window* window, osg::ref_ptr v break; default: - std::ios::fmtflags f(std::cerr.flags()); - std::cerr << "Unhandled SDL event of type 0x" << std::hex << evt.type << std::endl; - std::cerr.flags(f); + Log(Debug::Info) << "Unhandled SDL event of type 0x" << std::hex << evt.type; break; } } diff --git a/components/sdlutil/sdlvideowrapper.cpp b/components/sdlutil/sdlvideowrapper.cpp index dd89d1072..c2963be86 100644 --- a/components/sdlutil/sdlvideowrapper.cpp +++ b/components/sdlutil/sdlvideowrapper.cpp @@ -1,6 +1,6 @@ #include "sdlvideowrapper.hpp" -#include +#include #include @@ -65,7 +65,7 @@ namespace SDLUtil red[i] = green[i] = blue[i] = static_cast(value); } if (SDL_SetWindowGammaRamp(mWindow, red, green, blue) < 0) - std::cout << "Couldn't set gamma: " << SDL_GetError() << std::endl; + Log(Debug::Warning) << "Couldn't set gamma: " << SDL_GetError(); } void VideoWrapper::setVideoMode(int width, int height, bool fullscreen, bool windowBorder) diff --git a/components/settings/settings.cpp b/components/settings/settings.cpp index 15a222d31..66e5dfc04 100644 --- a/components/settings/settings.cpp +++ b/components/settings/settings.cpp @@ -1,8 +1,8 @@ #include "settings.hpp" #include -#include +#include #include #include @@ -69,7 +69,7 @@ public: mFile = file; boost::filesystem::ifstream stream; stream.open(boost::filesystem::path(file)); - std::cout << "Loading settings file: " << file << std::endl; + Log(Debug::Info) << "Loading settings file: " << file; std::string currentCategory; mLine = 0; while (!stream.eof() && !stream.fail()) @@ -186,8 +186,8 @@ public: // Ensure that all options in the current category have been written. for (CategorySettingStatusMap::iterator mit = written.begin(); mit != written.end(); ++mit) { if (mit->second == false && mit->first.first == currentCategory) { - std::cout << "Added new setting: [" << currentCategory << "] " - << mit->first.second << " = " << settings[mit->first] << std::endl; + Log(Debug::Verbose) << "Added new setting: [" << currentCategory << "] " + << mit->first.second << " = " << settings[mit->first]; ostream << mit->first.second << " = " << settings[mit->first] << std::endl; mit->second = true; changed = true; @@ -200,7 +200,7 @@ public: // Write the (new) current category to the file. ostream << "[" << currentCategory << "]" << std::endl; - //std::cout << "Wrote category: " << currentCategory << std::endl; + // Log(Debug::Verbose) << "Wrote category: " << currentCategory; // A setting can apparently follow the category on an input line. That's rather // inconvenient, since it makes it more likely to have duplicative sections, @@ -259,8 +259,8 @@ public: finder->second = true; // Did we really change it? if (value != settings[key]) { - std::cout << "Changed setting: [" << currentCategory << "] " - << setting << " = " << settings[key] << std::endl; + Log(Debug::Verbose) << "Changed setting: [" << currentCategory << "] " + << setting << " = " << settings[key]; changed = true; } // No need to write the current line, because we just emitted a replacement. @@ -276,8 +276,8 @@ public: // the current category at the end of the file before moving on to any new categories. for (CategorySettingStatusMap::iterator mit = written.begin(); mit != written.end(); ++mit) { if (mit->second == false && mit->first.first == currentCategory) { - std::cout << "Added new setting: [" << mit->first.first << "] " - << mit->first.second << " = " << settings[mit->first] << std::endl; + Log(Debug::Verbose) << "Added new setting: [" << mit->first.first << "] " + << mit->first.second << " = " << settings[mit->first]; ostream << mit->first.second << " = " << settings[mit->first] << std::endl; mit->second = true; changed = true; @@ -305,12 +305,12 @@ public: // If the catgory has changed, write a new category header. if (mit->first.first != currentCategory) { currentCategory = mit->first.first; - std::cout << "Created new setting section: " << mit->first.first << std::endl; + Log(Debug::Verbose) << "Created new setting section: " << mit->first.first; ostream << std::endl; ostream << "[" << currentCategory << "]" << std::endl; } - std::cout << "Added new setting: [" << mit->first.first << "] " - << mit->first.second << " = " << settings[mit->first] << std::endl; + Log(Debug::Verbose) << "Added new setting: [" << mit->first.first << "] " + << mit->first.second << " = " << settings[mit->first]; // Then write the setting. No need to mark it as written because we're done. ostream << mit->first.second << " = " << settings[mit->first] << std::endl; changed = true; @@ -319,7 +319,7 @@ public: // Now install the newly written file in the requested place. if (changed) { - std::cout << "Updating settings file: " << ipath << std::endl; + Log(Debug::Info) << "Updating settings file: " << ipath; boost::filesystem::ofstream ofstream; ofstream.open(ipath); ofstream << ostream.rdbuf(); diff --git a/components/shader/shadermanager.cpp b/components/shader/shadermanager.cpp index 5efd1b86e..28f4300c2 100644 --- a/components/shader/shadermanager.cpp +++ b/components/shader/shadermanager.cpp @@ -11,6 +11,8 @@ #include #include +#include + namespace Shader { @@ -31,13 +33,13 @@ namespace Shader size_t start = source.find('"', foundPos); if (start == std::string::npos || start == source.size()-1) { - std::cerr << "Invalid #include " << std::endl; + Log(Debug::Error) << "Invalid #include"; return false; } size_t end = source.find('"', start+1); if (end == std::string::npos) { - std::cerr << "Invalid #include " << std::endl; + Log(Debug::Error) << "Invalid #include"; return false; } std::string includeFilename = source.substr(start+1, end-(start+1)); @@ -46,7 +48,7 @@ namespace Shader includeFstream.open(includePath); if (includeFstream.fail()) { - std::cerr << "Failed to open " << includePath.string() << std::endl; + Log(Debug::Error) << "Failed to open " << includePath.string(); return false; } @@ -65,7 +67,7 @@ namespace Shader if (includedFiles.insert(includePath).second == false) { - std::cerr << "Detected cyclic #includes" << std::endl; + Log(Debug::Error) << "Detected cyclic #includes"; return false; } } @@ -81,14 +83,14 @@ namespace Shader size_t endPos = source.find_first_of(" \n\r()[].;", foundPos); if (endPos == std::string::npos) { - std::cerr << "Unexpected EOF" << std::endl; + Log(Debug::Error) << "Unexpected EOF"; return false; } std::string define = source.substr(foundPos+1, endPos - (foundPos+1)); ShaderManager::DefineMap::const_iterator defineFound = defines.find(define); if (defineFound == defines.end()) { - std::cerr << "Undefined " << define << std::endl; + Log(Debug::Error) << "Undefined " << define; return false; } else @@ -112,7 +114,7 @@ namespace Shader stream.open(p); if (stream.fail()) { - std::cerr << "Failed to open " << p.string() << std::endl; + Log(Debug::Error) << "Failed to open " << p.string(); return NULL; } std::stringstream buffer; diff --git a/components/shader/shadervisitor.cpp b/components/shader/shadervisitor.cpp index 9b3876d6c..cbd950ea3 100644 --- a/components/shader/shadervisitor.cpp +++ b/components/shader/shadervisitor.cpp @@ -10,6 +10,7 @@ #include +#include #include #include #include @@ -147,7 +148,7 @@ namespace Shader specularMap = texture; } else - std::cerr << "ShaderVisitor encountered unknown texture " << texture << std::endl; + Log(Debug::Error) << "ShaderVisitor encountered unknown texture " << texture; } } } diff --git a/components/to_utf8/to_utf8.cpp b/components/to_utf8/to_utf8.cpp index 8af0bc5ed..d4ab00381 100644 --- a/components/to_utf8/to_utf8.cpp +++ b/components/to_utf8/to_utf8.cpp @@ -2,10 +2,11 @@ #include #include -#include #include #include +#include + /* This file contains the code to translate from WINDOWS-1252 (native charset used in English version of Morrowind) to UTF-8. The library is designed to be extened to support more source encodings later, @@ -319,9 +320,7 @@ void Utf8Encoder::copyFromArray2(const char*& chp, char* &out) } } - std::ios::fmtflags f(std::cout.flags()); - std::cout << "Could not find glyph " << std::hex << (int)ch << " " << (int)ch2 << " " << (int)ch3 << std::endl; - std::cout.flags(f); + Log(Debug::Info) << "Could not find glyph " << std::hex << (int)ch << " " << (int)ch2 << " " << (int)ch3; *(out++) = ch; // Could not find glyph, just put whatever } diff --git a/components/vfs/filesystemarchive.cpp b/components/vfs/filesystemarchive.cpp index 9738e2a17..ce4ff020e 100644 --- a/components/vfs/filesystemarchive.cpp +++ b/components/vfs/filesystemarchive.cpp @@ -1,9 +1,9 @@ #include "filesystemarchive.hpp" -#include - #include +#include + namespace VFS { @@ -41,7 +41,7 @@ namespace VFS std::transform(proper.begin() + prefix, proper.end(), std::back_inserter(searchable), normalize_function); if (!mIndex.insert (std::make_pair (searchable, file)).second) - std::cerr << "Warning: found duplicate file for '" << proper << "', please check your file system for two files with the same name in different cases." << std::endl; + Log(Debug::Warning) << "Warning: found duplicate file for '" << proper << "', please check your file system for two files with the same name in different cases."; } mBuiltIndex = true; diff --git a/components/vfs/registerarchives.cpp b/components/vfs/registerarchives.cpp index a22e785cd..80e639f35 100644 --- a/components/vfs/registerarchives.cpp +++ b/components/vfs/registerarchives.cpp @@ -1,9 +1,10 @@ #include "registerarchives.hpp" #include -#include #include +#include + #include #include #include @@ -21,7 +22,7 @@ namespace VFS { // Last BSA has the highest priority const std::string archivePath = collections.getPath(*archive).string(); - std::cout << "Adding BSA archive " << archivePath << std::endl; + Log(Debug::Info) << "Adding BSA archive " << archivePath; vfs->addArchive(new BsaArchive(archivePath)); } @@ -40,12 +41,12 @@ namespace VFS { if (seen.insert(*iter).second) { - std::cout << "Adding data directory " << iter->string() << std::endl; + Log(Debug::Info) << "Adding data directory " << iter->string(); // Last data dir has the highest priority vfs->addArchive(new FileSystemArchive(iter->string())); } else - std::cerr << "Ignoring duplicate data directory " << iter->string() << std::endl; + Log(Debug::Info) << "Ignoring duplicate data directory " << iter->string(); } } diff --git a/components/widgets/imagebutton.cpp b/components/widgets/imagebutton.cpp index a3b0ae28a..2ea494ebd 100644 --- a/components/widgets/imagebutton.cpp +++ b/components/widgets/imagebutton.cpp @@ -2,6 +2,8 @@ #include +#include + namespace Gui { @@ -77,7 +79,7 @@ namespace Gui MyGUI::ITexture* texture = MyGUI::RenderManager::getInstance().getTexture(mImageNormal); if (!texture) { - std::cerr << "ImageButton: can't find " << mImageNormal << std::endl; + Log(Debug::Error) << "ImageButton: can't find image " << mImageNormal; return MyGUI::IntSize(0,0); } return MyGUI::IntSize (texture->getWidth(), texture->getHeight()); From c7a5548475f3495d6e1d711e943f4ebbbbf9dcc3 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 14 Aug 2018 20:01:09 +0400 Subject: [PATCH 41/53] Use new logging system for editor --- apps/opencs/editor.cpp | 10 +++++----- apps/opencs/main.cpp | 3 +-- apps/opencs/model/doc/document.cpp | 5 +++-- apps/opencs/model/world/refcollection.cpp | 10 +++++----- apps/opencs/view/render/object.cpp | 4 ++-- components/files/configurationmanager.cpp | 7 +++---- 6 files changed, 19 insertions(+), 20 deletions(-) diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index 73208b926..e733d9924 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -5,8 +5,8 @@ #include #include +#include #include - #include #include "model/doc/document.hpp" @@ -294,7 +294,7 @@ bool CS::Editor::makeIPCServer() mLock = boost::interprocess::file_lock(mPid.string().c_str()); if(!mLock.try_lock()) { - std::cerr << "OpenCS already running." << std::endl; + Log(Debug::Error) << "Error: OpenMW-CS is already running."; return false; } @@ -317,17 +317,17 @@ bool CS::Editor::makeIPCServer() if(boost::filesystem::exists(fullPath.toUtf8().constData())) { // TODO: compare pid of the current process with that in the file - std::cout << "Detected unclean shutdown." << std::endl; + Log(Debug::Info) << "Detected unclean shutdown."; // delete the stale file if(remove(fullPath.toUtf8().constData())) - std::cerr << "ERROR removing stale connection file" << std::endl; + Log(Debug::Error) << "Error: can not remove stale connection file."; } } } catch(const std::exception& e) { - std::cerr << "ERROR " << e.what() << std::endl; + Log(Debug::Error) << "Error: " << e.what(); return false; } diff --git a/apps/opencs/main.cpp b/apps/opencs/main.cpp index 6058e73f9..0473291ce 100644 --- a/apps/opencs/main.cpp +++ b/apps/opencs/main.cpp @@ -1,7 +1,6 @@ #include "editor.hpp" #include -#include #include #include @@ -31,7 +30,7 @@ class Application : public QApplication } catch (const std::exception& exception) { - std::cerr << "An exception has been caught: " << exception.what() << std::endl; + Log(Debug::Error) << "An exception has been caught: " << exception.what(); } return false; diff --git a/apps/opencs/model/doc/document.cpp b/apps/opencs/model/doc/document.cpp index e45d13aa9..233b3e439 100644 --- a/apps/opencs/model/doc/document.cpp +++ b/apps/opencs/model/doc/document.cpp @@ -2,7 +2,6 @@ #include #include -#include #include #include @@ -13,6 +12,8 @@ #include #endif +#include + void CSMDoc::Document::addGmsts() { for (size_t i=0; i < CSMWorld::DefaultGmsts::FloatCount; ++i) @@ -435,7 +436,7 @@ void CSMDoc::Document::modificationStateChanged (bool clean) void CSMDoc::Document::reportMessage (const CSMDoc::Message& message, int type) { /// \todo find a better way to get these messages to the user. - std::cout << message.mMessage << std::endl; + Log(Debug::Info) << message.mMessage; } void CSMDoc::Document::operationDone2 (int type, bool failed) diff --git a/apps/opencs/model/world/refcollection.cpp b/apps/opencs/model/world/refcollection.cpp index 6b586dcec..7767cca6d 100644 --- a/apps/opencs/model/world/refcollection.cpp +++ b/apps/opencs/model/world/refcollection.cpp @@ -1,8 +1,8 @@ #include "refcollection.hpp" #include -#include +#include #include #include @@ -58,10 +58,10 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool // message if (index.first != mref.mTarget[0] || index.second != mref.mTarget[1]) { - std::cerr << "The Position of moved ref " - << ref.mRefID << " does not match the target cell" << std::endl; - std::cerr << "Position: #" << index.first << " " << index.second - <<", Target #"<< mref.mTarget[0] << " " << mref.mTarget[1] << std::endl; + Log(Debug::Warning) << "Warning: the Position of moved ref " + << ref.mRefID << " does not match the target cell"; + Log(Debug::Warning) << "Position: #" << index.first << " " << index.second + <<", Target #"<< mref.mTarget[0] << " " << mref.mTarget[1]; stream.clear(); stream << "#" << mref.mTarget[0] << " " << mref.mTarget[1]; diff --git a/apps/opencs/view/render/object.cpp b/apps/opencs/view/render/object.cpp index 961b6c1c1..8301f4e9e 100644 --- a/apps/opencs/view/render/object.cpp +++ b/apps/opencs/view/render/object.cpp @@ -1,7 +1,6 @@ #include "object.hpp" #include -#include #include #include @@ -24,6 +23,7 @@ #include "../../model/world/cellcoordinates.hpp" #include "../../model/prefs/state.hpp" +#include #include #include #include @@ -133,7 +133,7 @@ void CSVRender::Object::update() catch (std::exception& e) { // TODO: use error marker mesh - std::cerr << e.what() << std::endl; + Log(Debug::Error) << e.what(); } } diff --git a/components/files/configurationmanager.cpp b/components/files/configurationmanager.cpp index 333ec5ced..c58130f96 100644 --- a/components/files/configurationmanager.cpp +++ b/components/files/configurationmanager.cpp @@ -133,7 +133,7 @@ bool ConfigurationManager::loadConfig(const boost::filesystem::path& path, if (boost::filesystem::is_regular_file(cfgFile)) { if (!mSilent) - Log(Debug::Info) << "Loading config file: " << cfgFile.string() << "... "; + Log(Debug::Info) << "Loading config file: " << cfgFile.string(); boost::filesystem::ifstream configFileStreamUnfiltered(cfgFile); boost::iostreams::filtering_istream configFileStream; @@ -144,14 +144,13 @@ bool ConfigurationManager::loadConfig(const boost::filesystem::path& path, boost::program_options::store(boost::program_options::parse_config_file( configFileStream, description, true), variables); - if (!mSilent) - Log(Debug::Info) << "done."; return true; } else { if (!mSilent) - Log(Debug::Info) << "failed."; + Log(Debug::Error) << "Loading failed."; + return false; } } From d19cbdb652dd135230714f6b1cb6dd94af678260 Mon Sep 17 00:00:00 2001 From: Yohaulticetl Date: Fri, 27 Jul 2018 21:13:04 -0300 Subject: [PATCH 42/53] Factored strength into hand-to-hand combat --- apps/launcher/advancedpage.cpp | 2 ++ apps/openmw/mwmechanics/combat.cpp | 6 +++--- files/settings-default.cfg | 3 +++ files/ui/advancedpage.ui | 6 ++++++ 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/apps/launcher/advancedpage.cpp b/apps/launcher/advancedpage.cpp index ff229e02d..5406201b9 100644 --- a/apps/launcher/advancedpage.cpp +++ b/apps/launcher/advancedpage.cpp @@ -76,6 +76,7 @@ bool Launcher::AdvancedPage::loadSettings() loadSettingBool(chargeForEveryFollowerCheckBox, "charge for every follower travelling", "Game"); loadSettingBool(enchantedWeaponsMagicalCheckBox, "enchanted weapons are magical", "Game"); loadSettingBool(permanentBarterDispositionChangeCheckBox, "barter disposition change is permanent", "Game"); + loadSettingBool(strengthInfluencesHandToHand, "strength influences hand to hand", "Game"); // Input Settings loadSettingBool(allowThirdPersonZoomCheckBox, "allow third person zoom", "Input"); @@ -131,6 +132,7 @@ void Launcher::AdvancedPage::saveSettings() saveSettingBool(chargeForEveryFollowerCheckBox, "charge for every follower travelling", "Game"); saveSettingBool(enchantedWeaponsMagicalCheckBox, "enchanted weapons are magical", "Game"); saveSettingBool(permanentBarterDispositionChangeCheckBox, "barter disposition change is permanent", "Game"); + saveSettingBool(strengthInfluencesHandToHand, "strength influences hand to hand", "Game"); // Input Settings saveSettingBool(allowThirdPersonZoomCheckBox, "allow third person zoom", "Input"); diff --git a/apps/openmw/mwmechanics/combat.cpp b/apps/openmw/mwmechanics/combat.cpp index 5910dbacd..51e7eefdd 100644 --- a/apps/openmw/mwmechanics/combat.cpp +++ b/apps/openmw/mwmechanics/combat.cpp @@ -388,14 +388,14 @@ namespace MWMechanics void getHandToHandDamage(const MWWorld::Ptr &attacker, const MWWorld::Ptr &victim, float &damage, bool &healthdmg, float attackStrength) { - // Note: MCP contains an option to include Strength in hand-to-hand damage - // calculations. Some mods recommend using it, so we may want to include an - // option for it. const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); float minstrike = store.get().find("fMinHandToHandMult")->getFloat(); float maxstrike = store.get().find("fMaxHandToHandMult")->getFloat(); damage = static_cast(attacker.getClass().getSkill(attacker, ESM::Skill::HandToHand)); damage *= minstrike + ((maxstrike-minstrike)*attackStrength); + if (Settings::Manager::getBool("strength influences hand to hand", "Game")){ + damage *= attacker.getClass().getCreatureStats(attacker).getAttribute(ESM::Attribute::Strength).getModified() / 40.0f; + } MWMechanics::CreatureStats& otherstats = victim.getClass().getCreatureStats(victim); healthdmg = otherstats.isParalyzed() diff --git a/files/settings-default.cfg b/files/settings-default.cfg index b0ddd5223..d974d8b68 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -225,6 +225,9 @@ use additional anim sources = false # Make the disposition change of merchants caused by barter dealings permanent barter disposition change is permanent = false +# Factors Strength into hand-to-hand combat. Uses the MCP formula (damage * (strength / 40)). +strength influences hand to hand = false + [General] # Anisotropy reduces distortion in textures at low angles (e.g. 0 to 16). diff --git a/files/ui/advancedpage.ui b/files/ui/advancedpage.ui index 553c244d3..811bc5f86 100644 --- a/files/ui/advancedpage.ui +++ b/files/ui/advancedpage.ui @@ -99,6 +99,12 @@ Barter disposition change is permanent + + + <html><head/><body><p>Uses the MCP formula (damage * (strength / 40)) to factor the Strength attribute into hand-to-hand combat.</p><p>The default value is false.</p></body></html> + + + Factor strength into hand-to-hand combat From 18e51e0e981d8391d3925b5f18b97469a5be9387 Mon Sep 17 00:00:00 2001 From: Yohaulticetl Date: Sat, 28 Jul 2018 10:10:01 -0300 Subject: [PATCH 43/53] Added check for werewolves --- apps/openmw/mwmechanics/combat.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwmechanics/combat.cpp b/apps/openmw/mwmechanics/combat.cpp index 51e7eefdd..9930c8206 100644 --- a/apps/openmw/mwmechanics/combat.cpp +++ b/apps/openmw/mwmechanics/combat.cpp @@ -393,14 +393,16 @@ namespace MWMechanics float maxstrike = store.get().find("fMaxHandToHandMult")->getFloat(); damage = static_cast(attacker.getClass().getSkill(attacker, ESM::Skill::HandToHand)); damage *= minstrike + ((maxstrike-minstrike)*attackStrength); - if (Settings::Manager::getBool("strength influences hand to hand", "Game")){ - damage *= attacker.getClass().getCreatureStats(attacker).getAttribute(ESM::Attribute::Strength).getModified() / 40.0f; - } MWMechanics::CreatureStats& otherstats = victim.getClass().getCreatureStats(victim); healthdmg = otherstats.isParalyzed() || otherstats.getKnockedDown(); bool isWerewolf = (attacker.getClass().isNpc() && attacker.getClass().getNpcStats(attacker).isWerewolf()); + + if (Settings::Manager::getBool("strength influences hand to hand", "Game") && !isWerewolf) { + damage *= attacker.getClass().getCreatureStats(attacker).getAttribute(ESM::Attribute::Strength).getModified() / 40.0f; + } + if(isWerewolf) { healthdmg = true; From 7e9ce99062e409ce578901f2dabba24a35965a07 Mon Sep 17 00:00:00 2001 From: Yohaulticetl Date: Sat, 28 Jul 2018 23:13:56 -0300 Subject: [PATCH 44/53] Made the werewolf check optional --- apps/launcher/advancedpage.cpp | 6 ++++ apps/openmw/mwmechanics/combat.cpp | 7 +++- files/settings-default.cfg | 6 ++-- files/ui/advancedpage.ui | 53 +++++++++++++++++++++++++++--- 4 files changed, 65 insertions(+), 7 deletions(-) diff --git a/apps/launcher/advancedpage.cpp b/apps/launcher/advancedpage.cpp index 5406201b9..9cb89d2b5 100644 --- a/apps/launcher/advancedpage.cpp +++ b/apps/launcher/advancedpage.cpp @@ -77,6 +77,9 @@ bool Launcher::AdvancedPage::loadSettings() loadSettingBool(enchantedWeaponsMagicalCheckBox, "enchanted weapons are magical", "Game"); loadSettingBool(permanentBarterDispositionChangeCheckBox, "barter disposition change is permanent", "Game"); loadSettingBool(strengthInfluencesHandToHand, "strength influences hand to hand", "Game"); + int unarmedFactorsStrengthIndex = mEngineSettings.getInt("strength influences hand to hand", "Game"); + if (unarmedFactorsStrengthIndex >= 0 && unarmedFactorsStrengthIndex <= 2) + unarmedFactorsStrengthComboBox->setCurrentIndex(unarmedFactorsStrengthIndex); // Input Settings loadSettingBool(allowThirdPersonZoomCheckBox, "allow third person zoom", "Input"); @@ -133,6 +136,9 @@ void Launcher::AdvancedPage::saveSettings() saveSettingBool(enchantedWeaponsMagicalCheckBox, "enchanted weapons are magical", "Game"); saveSettingBool(permanentBarterDispositionChangeCheckBox, "barter disposition change is permanent", "Game"); saveSettingBool(strengthInfluencesHandToHand, "strength influences hand to hand", "Game"); + int unarmedFactorsStrengthIndex = unarmedFactorsStrengthComboBox->currentIndex(); + if (unarmedFactorsStrengthIndex != mEngineSettings.getInt("strength influences hand to hand", "Game")) + mEngineSettings.setInt("strength influences hand to hand", "Game", unarmedFactorsStrengthIndex); // Input Settings saveSettingBool(allowThirdPersonZoomCheckBox, "allow third person zoom", "Input"); diff --git a/apps/openmw/mwmechanics/combat.cpp b/apps/openmw/mwmechanics/combat.cpp index 9930c8206..06ed99f5a 100644 --- a/apps/openmw/mwmechanics/combat.cpp +++ b/apps/openmw/mwmechanics/combat.cpp @@ -399,7 +399,12 @@ namespace MWMechanics || otherstats.getKnockedDown(); bool isWerewolf = (attacker.getClass().isNpc() && attacker.getClass().getNpcStats(attacker).isWerewolf()); - if (Settings::Manager::getBool("strength influences hand to hand", "Game") && !isWerewolf) { + // Options in the launcher's combo box: unarmedFactorsStrengthComboBox + // 0 = Do not factor strength into hand-to-hand combat. + // 1 = Factor into werewolf hand-to-hand combat. + // 2 = Ignore werewolves. + int factorStrength = Settings::Manager::getInt("strength influences hand to hand", "Game"); + if (factorStrength == 1 || (factorStrength == 2 && !isWerewolf)) { damage *= attacker.getClass().getCreatureStats(attacker).getAttribute(ESM::Attribute::Strength).getModified() / 40.0f; } diff --git a/files/settings-default.cfg b/files/settings-default.cfg index d974d8b68..c2ac2eb1c 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -225,8 +225,10 @@ use additional anim sources = false # Make the disposition change of merchants caused by barter dealings permanent barter disposition change is permanent = false -# Factors Strength into hand-to-hand combat. Uses the MCP formula (damage * (strength / 40)). -strength influences hand to hand = false +# Uses the MCP formula (damage * (strength / 40)) to factor Strength into hand-to-hand combat. +# (0 means it does not factor it in, 1 means it factors into werewolves damage calculation and +# 2 means werewolves are ignored) +strength influences hand to hand = 0 [General] diff --git a/files/ui/advancedpage.ui b/files/ui/advancedpage.ui index 811bc5f86..bd5bbf0e7 100644 --- a/files/ui/advancedpage.ui +++ b/files/ui/advancedpage.ui @@ -100,12 +100,57 @@ Barter disposition change is permanent + + - <html><head/><body><p>Uses the MCP formula (damage * (strength / 40)) to factor the Strength attribute into hand-to-hand combat.</p><p>The default value is false.</p></body></html> - - - Factor strength into hand-to-hand combat + <html><head/><body><p>Factor strength into hand-to-hand damage calculations, as the MCP formula: damage * (strength / 40).</p><p>The default value is Off.</p></body></html> + + + -1 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Factor strength into hand-to-hand combat: + + + + + + + 1 + + + + Off + + + + + Affect werewolves + + + + + Do not affect werewolves + + + + + From d6f607e823cc806e4f7f00c88beff464ffb666e4 Mon Sep 17 00:00:00 2001 From: Yohaulticetl Date: Sat, 28 Jul 2018 23:47:35 -0300 Subject: [PATCH 45/53] Fixed wrong starting index --- files/ui/advancedpage.ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/files/ui/advancedpage.ui b/files/ui/advancedpage.ui index bd5bbf0e7..76c007dc5 100644 --- a/files/ui/advancedpage.ui +++ b/files/ui/advancedpage.ui @@ -131,7 +131,7 @@ - 1 + 0 From 640b32da5621d7aa2c4e178d30a04128ab6b6122 Mon Sep 17 00:00:00 2001 From: Yohaulticetl Date: Thu, 2 Aug 2018 21:29:41 -0300 Subject: [PATCH 46/53] Fixed merge conflicts --- apps/launcher/advancedpage.cpp | 2 ++ files/ui/advancedpage.ui | 10 ++++++++++ 2 files changed, 12 insertions(+) diff --git a/apps/launcher/advancedpage.cpp b/apps/launcher/advancedpage.cpp index 9cb89d2b5..b254e0b01 100644 --- a/apps/launcher/advancedpage.cpp +++ b/apps/launcher/advancedpage.cpp @@ -81,6 +81,7 @@ bool Launcher::AdvancedPage::loadSettings() if (unarmedFactorsStrengthIndex >= 0 && unarmedFactorsStrengthIndex <= 2) unarmedFactorsStrengthComboBox->setCurrentIndex(unarmedFactorsStrengthIndex); + // Input Settings loadSettingBool(allowThirdPersonZoomCheckBox, "allow third person zoom", "Input"); loadSettingBool(grabCursorCheckBox, "grab cursor", "Input"); @@ -140,6 +141,7 @@ void Launcher::AdvancedPage::saveSettings() if (unarmedFactorsStrengthIndex != mEngineSettings.getInt("strength influences hand to hand", "Game")) mEngineSettings.setInt("strength influences hand to hand", "Game", unarmedFactorsStrengthIndex); + // Input Settings saveSettingBool(allowThirdPersonZoomCheckBox, "allow third person zoom", "Input"); saveSettingBool(grabCursorCheckBox, "grab cursor", "Input"); diff --git a/files/ui/advancedpage.ui b/files/ui/advancedpage.ui index 76c007dc5..b308f21ac 100644 --- a/files/ui/advancedpage.ui +++ b/files/ui/advancedpage.ui @@ -22,7 +22,11 @@ 0 0 641 +<<<<<<< HEAD 998 +======= + 968 +>>>>>>> Fixed merge conflicts @@ -89,6 +93,7 @@ Enchanted weapons are magical +<<<<<<< HEAD @@ -100,6 +105,11 @@ Barter disposition change is permanent +======= + + + +>>>>>>> Fixed merge conflicts From 4ff6f64ecddba69325824d555cc80ace63e8f0a2 Mon Sep 17 00:00:00 2001 From: Yohaulticetl Date: Sat, 11 Aug 2018 19:24:46 -0300 Subject: [PATCH 47/53] Added entry to the changelog. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f1f80aa84..c8eef0d45 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -112,6 +112,7 @@ Feature #4548: Weapon priority: use the actual chance to hit the target instead of weapon skill Feature #4549: Weapon priority: use the actual damage in weapon rating calculations Feature #4550: Weapon priority: make ranged weapon bonus more sensible + Feature #4579: Add option for applying Strength into hand to hand damage Feature #4581: Use proper logging system Task #2490: Don't open command prompt window on Release-mode builds automatically Task #4545: Enable is_pod string test From ffb103f7c5d66e034b3b6b2c026592571c3c1967 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 12 Aug 2018 11:08:05 +0200 Subject: [PATCH 48/53] updated credits file --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index d2fd05180..a722ff723 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -171,6 +171,7 @@ Programmers viadanna Vincent Heuken vocollapse + Yohaulticetl zelurker Documentation From 5a4d0cec3a70e5f6e60ade7547f59cfd0a0c03d5 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 14 Aug 2018 23:05:43 +0400 Subject: [PATCH 49/53] Use new logging system for game itself --- apps/openmw/engine.cpp | 34 ++++---- apps/openmw/main.cpp | 8 +- apps/openmw/mwclass/creature.cpp | 4 +- apps/openmw/mwclass/npc.cpp | 5 +- apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 13 ++- apps/openmw/mwdialogue/scripttest.cpp | 11 +-- apps/openmw/mwgui/charactercreation.cpp | 5 +- apps/openmw/mwgui/class.cpp | 4 +- apps/openmw/mwgui/dialogue.cpp | 3 +- apps/openmw/mwgui/formatting.cpp | 3 +- apps/openmw/mwgui/loadingscreen.cpp | 5 +- apps/openmw/mwgui/messagebox.cpp | 3 +- apps/openmw/mwgui/race.cpp | 3 +- apps/openmw/mwgui/savegamedialog.cpp | 6 +- apps/openmw/mwgui/settingswindow.cpp | 6 +- apps/openmw/mwgui/sortfilteritemmodel.cpp | 6 +- apps/openmw/mwgui/spellmodel.cpp | 4 +- apps/openmw/mwgui/videowidget.cpp | 3 +- apps/openmw/mwgui/windowmanagerimp.cpp | 6 +- apps/openmw/mwinput/inputmanagerimp.cpp | 5 +- apps/openmw/mwmechanics/actors.cpp | 4 +- apps/openmw/mwmechanics/aisequence.cpp | 4 +- apps/openmw/mwmechanics/aiwander.cpp | 7 +- apps/openmw/mwmechanics/levelledlist.hpp | 5 +- apps/openmw/mwmechanics/objects.cpp | 4 +- apps/openmw/mwmechanics/summoning.cpp | 4 +- apps/openmw/mwphysics/physicssystem.cpp | 7 +- apps/openmw/mwrender/animation.cpp | 12 +-- apps/openmw/mwrender/bulletdebugdraw.cpp | 6 +- apps/openmw/mwrender/characterpreview.cpp | 4 +- apps/openmw/mwrender/creatureanimation.cpp | 6 +- apps/openmw/mwrender/globalmap.cpp | 12 +-- apps/openmw/mwrender/localmap.cpp | 14 ++-- apps/openmw/mwrender/npcanimation.cpp | 12 +-- apps/openmw/mwrender/renderingmanager.cpp | 6 +- apps/openmw/mwrender/water.cpp | 9 +- apps/openmw/mwscript/aiextensions.cpp | 19 ++--- apps/openmw/mwscript/containerextensions.cpp | 6 +- apps/openmw/mwscript/dialogueextensions.cpp | 8 +- apps/openmw/mwscript/globalscripts.cpp | 11 ++- apps/openmw/mwscript/locals.cpp | 10 +-- apps/openmw/mwscript/scriptmanagerimp.cpp | 13 +-- apps/openmw/mwscript/statsextensions.cpp | 8 +- .../mwscript/transformationextensions.cpp | 6 +- apps/openmw/mwsound/ffmpeg_decoder.cpp | 10 +-- apps/openmw/mwsound/openal_output.cpp | 83 ++++++++++--------- apps/openmw/mwsound/soundmanagerimp.cpp | 28 ++++--- apps/openmw/mwstate/statemanagerimp.cpp | 18 ++-- apps/openmw/mwworld/cellpreloader.cpp | 7 +- apps/openmw/mwworld/cells.cpp | 5 +- apps/openmw/mwworld/cellstore.cpp | 28 +++---- apps/openmw/mwworld/containerstore.cpp | 7 +- apps/openmw/mwworld/contentloader.hpp | 6 +- apps/openmw/mwworld/esmstore.cpp | 9 +- apps/openmw/mwworld/inventorystore.cpp | 3 +- apps/openmw/mwworld/livecellref.cpp | 10 +-- apps/openmw/mwworld/localscripts.cpp | 14 ++-- apps/openmw/mwworld/player.cpp | 7 +- apps/openmw/mwworld/projectilemanager.cpp | 5 +- apps/openmw/mwworld/scene.cpp | 14 ++-- apps/openmw/mwworld/store.cpp | 5 +- apps/openmw/mwworld/worldimp.cpp | 14 ++-- 62 files changed, 310 insertions(+), 297 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index bd40dc1ab..2941ede1b 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -10,6 +10,8 @@ #include +#include + #include #include @@ -61,7 +63,7 @@ namespace void checkSDLError(int ret) { if (ret != 0) - std::cerr << "SDL error: " << SDL_GetError() << std::endl; + Log(Debug::Error) << "SDL error: " << SDL_GetError(); } } @@ -189,7 +191,7 @@ bool OMW::Engine::frame(float frametime) } catch (const std::exception& e) { - std::cerr << "Error in frame: " << e.what() << std::endl; + Log(Debug::Error) << "Error in frame: " << e.what(); } return true; } @@ -364,7 +366,7 @@ void OMW::Engine::createWindow(Settings::Manager& settings) // Try with a lower AA if (antialiasing > 0) { - std::cout << "Note: " << antialiasing << "x antialiasing not supported, trying " << antialiasing/2 << std::endl; + Log(Debug::Warning) << "Warning: " << antialiasing << "x antialiasing not supported, trying " << antialiasing/2; antialiasing /= 2; Settings::Manager::setInt("antialiasing", "Video", antialiasing); checkSDLError(SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, antialiasing)); @@ -373,7 +375,7 @@ void OMW::Engine::createWindow(Settings::Manager& settings) else { std::stringstream error; - error << "Failed to create SDL window: " << SDL_GetError() << std::endl; + error << "Failed to create SDL window: " << SDL_GetError(); throw std::runtime_error(error.str()); } } @@ -420,16 +422,16 @@ void OMW::Engine::setWindowIcon() std::string windowIcon = (mResDir / "mygui" / "openmw.png").string(); windowIconStream.open(windowIcon, std::ios_base::in | std::ios_base::binary); if (windowIconStream.fail()) - std::cerr << "Error: Failed to open " << windowIcon << std::endl; + Log(Debug::Error) << "Error: Failed to open " << windowIcon; osgDB::ReaderWriter* reader = osgDB::Registry::instance()->getReaderWriterForExtension("png"); if (!reader) { - std::cerr << "Error: Failed to read window icon, no png readerwriter found" << std::endl; + Log(Debug::Error) << "Error: Failed to read window icon, no png readerwriter found"; return; } osgDB::ReaderWriter::ReadResult result = reader->readImage(windowIconStream); if (!result.success()) - std::cerr << "Error: Failed to read " << windowIcon << ": " << result.message() << " code " << result.status() << std::endl; + Log(Debug::Error) << "Error: Failed to read " << windowIcon << ": " << result.message() << " code " << result.status(); else { osg::ref_ptr image = result.getImage(); @@ -564,21 +566,19 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) { std::pair result = mEnvironment.getScriptManager()->compileAll(); if (result.first) - std::cout + Log(Debug::Info) << "compiled " << result.second << " of " << result.first << " scripts (" << 100*static_cast (result.second)/result.first - << "%)" - << std::endl; + << "%)"; } if (mCompileAllDialogue) { std::pair result = MWDialogue::ScriptTest::compileAll(&mExtensions, mWarningsMode); if (result.first) - std::cout + Log(Debug::Info) << "compiled " << result.second << " of " << result.first << " dialogue script/actor combinations a(" << 100*static_cast (result.second)/result.first - << "%)" - << std::endl; + << "%)"; } } @@ -614,14 +614,14 @@ public: osgDB::ReaderWriter* readerwriter = osgDB::Registry::instance()->getReaderWriterForExtension(mScreenshotFormat); if (!readerwriter) { - std::cerr << "Error: Can't write screenshot, no '" << mScreenshotFormat << "' readerwriter found" << std::endl; + Log(Debug::Error) << "Error: Can't write screenshot, no '" << mScreenshotFormat << "' readerwriter found"; return; } osgDB::ReaderWriter::WriteResult result = readerwriter->writeImage(image, outStream); if (!result.success()) { - std::cerr << "Error: Can't write screenshot: " << result.message() << " code " << result.status() << std::endl; + Log(Debug::Error) << "Error: Can't write screenshot: " << result.message() << " code " << result.status(); } } @@ -636,7 +636,7 @@ void OMW::Engine::go() { assert (!mContentFiles.empty()); - std::cout << "OSG version: " << osgGetVersion() << std::endl; + Log(Debug::Info) << "OSG version: " << osgGetVersion(); // Load settings Settings::Manager settings; @@ -738,7 +738,7 @@ void OMW::Engine::go() // Save user settings settings.saveUser(settingspath); - std::cout << "Quitting peacefully." << std::endl; + Log(Debug::Info) << "Quitting peacefully."; } void OMW::Engine::setCompileAll (bool all) diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index f9cf58544..176adfad0 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -1,5 +1,3 @@ -#include - #include #include #include @@ -202,8 +200,8 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat StringsVector content = variables["content"].as().toStdStringVector(); if (content.empty()) { - std::cout << "No content file given (esm/esp, nor omwgame/omwaddon). Aborting..." << std::endl; - return false; + Log(Debug::Error) << "No content file given (esm/esp, nor omwgame/omwaddon). Aborting..."; + return false; } StringsVector::const_iterator it(content.begin()); @@ -217,7 +215,7 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat engine.setCell(variables["start"].as().toStdString()); engine.setSkipMenu (variables["skip-menu"].as(), variables["new-game"].as()); if (!variables["skip-menu"].as() && variables["new-game"].as()) - std::cerr << "Warning: new-game used without skip-menu -> ignoring it" << std::endl; + Log(Debug::Warning) << "Warning: new-game used without skip-menu -> ignoring it"; // scripts engine.setCompileAll(variables["script-all"].as()); diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 02a8a17ef..161711751 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -1,7 +1,7 @@ #include "creature.hpp" #include - +#include #include #include #include @@ -146,7 +146,7 @@ namespace MWClass if (const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get().search(*iter)) data->mCreatureStats.getSpells().add (spell); else /// \todo add option to make this a fatal error message pop-up, but default to warning for vanilla compatibility - std::cerr << "Warning: ignoring nonexistent spell '" << *iter << "' on creature '" << ref->mBase->mId << "'" << std::endl; + Log(Debug::Warning) << "Warning: ignoring nonexistent spell '" << *iter << "' on creature '" << ref->mBase->mId << "'"; } // inventory diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 1c293f1e4..c06c3f67c 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -4,6 +4,7 @@ #include +#include #include #include #include @@ -365,7 +366,7 @@ namespace MWClass if (const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get().search(*iter)) data->mNpcStats.getSpells().add (spell); else - std::cerr << "Warning: ignoring nonexistent race power '" << *iter << "' on NPC '" << ref->mBase->mId << "'" << std::endl; + Log(Debug::Warning) << "Warning: ignoring nonexistent race power '" << *iter << "' on NPC '" << ref->mBase->mId << "'"; } if (!ref->mBase->mFaction.empty()) @@ -395,7 +396,7 @@ namespace MWClass else { /// \todo add option to make this a fatal error message pop-up, but default to warning for vanilla compatibility - std::cerr << "Warning: ignoring nonexistent spell '" << *iter << "' on NPC '" << ref->mBase->mId << "'" << std::endl; + Log(Debug::Warning) << "Warning: ignoring nonexistent spell '" << *iter << "' on NPC '" << ref->mBase->mId << "'"; } } diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index df214ce86..c3e56c0bf 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -5,7 +5,8 @@ #include #include #include -#include + +#include #include #include @@ -203,16 +204,14 @@ namespace MWDialogue } catch (const std::exception& error) { - std::cerr << std::string ("Dialogue error: An exception has been thrown: ") + error.what() << std::endl; + Log(Debug::Error) << std::string ("Dialogue error: An exception has been thrown: ") + error.what(); success = false; } if (!success) { - std::cerr - << "Warning: compiling failed (dialogue script)" << std::endl - << cmd - << std::endl << std::endl; + Log(Debug::Warning) + << "Warning: compiling failed (dialogue script)\n" << cmd << "\n\n"; } return success; @@ -232,7 +231,7 @@ namespace MWDialogue } catch (const std::exception& error) { - std::cerr << std::string ("Dialogue error: An exception has been thrown: ") + error.what() << std::endl; + Log(Debug::Error) << std::string ("Dialogue error: An exception has been thrown: ") + error.what(); } } } diff --git a/apps/openmw/mwdialogue/scripttest.cpp b/apps/openmw/mwdialogue/scripttest.cpp index 54b4d8cd9..4f0b7422c 100644 --- a/apps/openmw/mwdialogue/scripttest.cpp +++ b/apps/openmw/mwdialogue/scripttest.cpp @@ -1,7 +1,5 @@ #include "scripttest.hpp" -#include - #include "../mwworld/manualref.hpp" #include "../mwworld/esmstore.hpp" #include "../mwworld/class.hpp" @@ -12,6 +10,7 @@ #include "../mwscript/compilercontext.hpp" +#include #include #include #include @@ -80,16 +79,14 @@ void test(const MWWorld::Ptr& actor, int &compiled, int &total, const Compiler:: } catch (const std::exception& error) { - std::cerr << std::string ("Dialogue error: An exception has been thrown: ") + error.what() << std::endl; + Log(Debug::Error) << std::string ("Dialogue error: An exception has been thrown: ") + error.what(); success = false; } if (!success) { - std::cerr - << "compiling failed (dialogue script)" << std::endl - << info->mResultScript - << std::endl << std::endl; + Log(Debug::Warning) + << "compiling failed (dialogue script)\n" << info->mResultScript << "\n\n"; } } } diff --git a/apps/openmw/mwgui/charactercreation.cpp b/apps/openmw/mwgui/charactercreation.cpp index 2deb62215..e3effa995 100644 --- a/apps/openmw/mwgui/charactercreation.cpp +++ b/apps/openmw/mwgui/charactercreation.cpp @@ -1,5 +1,6 @@ #include "charactercreation.hpp" +#include #include #include "../mwbase/environment.hpp" @@ -284,7 +285,7 @@ namespace MWGui } catch (std::exception& e) { - std::cerr << "Error: Failed to create chargen window: " << e.what() << std::endl; + Log(Debug::Error) << "Error: Failed to create chargen window: " << e.what(); } } @@ -602,7 +603,7 @@ namespace MWGui mGenerateClass = "Mage"; else { - std::cout << "Failed to deduce class from chosen answers in generate class dialog" << std::endl; + Log(Debug::Warning) << "Failed to deduce class from chosen answers in generate class dialog."; mGenerateClass = "Thief"; } } diff --git a/apps/openmw/mwgui/class.cpp b/apps/openmw/mwgui/class.cpp index 4d2a15c82..6ed7a4491 100644 --- a/apps/openmw/mwgui/class.cpp +++ b/apps/openmw/mwgui/class.cpp @@ -12,6 +12,8 @@ #include "../mwworld/esmstore.hpp" +#include + #include "tooltips.hpp" namespace @@ -924,7 +926,7 @@ namespace MWGui std::string classImage = std::string("textures\\levelup\\") + classId + ".dds"; if (!MWBase::Environment::get().getWindowManager()->textureExists(classImage)) { - std::cout << "No class image for " << classId << ", falling back to default" << std::endl; + Log(Debug::Warning) << "No class image for " << classId << ", falling back to default"; classImage = "textures\\levelup\\warrior.dds"; } imageBox->setImageTexture(classImage); diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index 7b177fdb0..aeb6dfc0f 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -411,7 +412,7 @@ namespace MWGui { if (!actor.getClass().isActor()) { - std::cerr << "Warning: can not talk with non-actor object." << std::endl; + Log(Debug::Warning) << "Warning: can not talk with non-actor object."; return; } diff --git a/apps/openmw/mwgui/formatting.cpp b/apps/openmw/mwgui/formatting.cpp index edcb94eed..663bd7338 100644 --- a/apps/openmw/mwgui/formatting.cpp +++ b/apps/openmw/mwgui/formatting.cpp @@ -13,6 +13,7 @@ #include #include +#include #include #include @@ -300,7 +301,7 @@ namespace MWGui if (!exists) { - std::cerr << "Warning: Could not find \"" << src << "\" referenced by an tag." << std::endl; + Log(Debug::Warning) << "Warning: Could not find \"" << src << "\" referenced by an tag."; break; } diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp index 28f4b8890..be3d477e1 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -10,9 +10,8 @@ #include #include - +#include #include - #include #include @@ -94,7 +93,7 @@ namespace MWGui ++found; } if (mSplashScreens.empty()) - std::cerr << "No splash screens found!" << std::endl; + Log(Debug::Warning) << "Warning: no splash screens found!"; } void LoadingScreen::setLabel(const std::string &label, bool important) diff --git a/apps/openmw/mwgui/messagebox.cpp b/apps/openmw/mwgui/messagebox.cpp index a79112b9f..2fbce97d4 100644 --- a/apps/openmw/mwgui/messagebox.cpp +++ b/apps/openmw/mwgui/messagebox.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include "../mwbase/environment.hpp" @@ -125,7 +126,7 @@ namespace MWGui { if (mInterMessageBoxe != NULL) { - std::cerr << "Warning: replacing an interactive message box that was not answered yet" << std::endl; + Log(Debug::Warning) << "Warning: replacing an interactive message box that was not answered yet"; mInterMessageBoxe->setVisible(false); delete mInterMessageBoxe; mInterMessageBoxe = NULL; diff --git a/apps/openmw/mwgui/race.cpp b/apps/openmw/mwgui/race.cpp index 64609cbe6..96c0d7de4 100644 --- a/apps/openmw/mwgui/race.cpp +++ b/apps/openmw/mwgui/race.cpp @@ -6,6 +6,7 @@ #include +#include #include #include "../mwworld/esmstore.hpp" @@ -340,7 +341,7 @@ namespace MWGui } catch (std::exception& e) { - std::cerr << "Error creating preview: " << e.what() << std::endl; + Log(Debug::Error) << "Error creating preview: " << e.what(); } } diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index 43e511144..45790cbf5 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -12,6 +12,8 @@ #include #include +#include + #include #include @@ -438,14 +440,14 @@ namespace MWGui osgDB::ReaderWriter* readerwriter = osgDB::Registry::instance()->getReaderWriterForExtension("jpg"); if (!readerwriter) { - std::cerr << "Error: Can't open savegame screenshot, no jpg readerwriter found" << std::endl; + Log(Debug::Error) << "Error: Can't open savegame screenshot, no jpg readerwriter found"; return; } osgDB::ReaderWriter::ReadResult result = readerwriter->readImage(instream); if (!result.success()) { - std::cerr << "Error: Failed to read savegame screenshot: " << result.message() << " code " << result.status() << std::endl; + Log(Debug::Error) << "Error: Failed to read savegame screenshot: " << result.message() << " code " << result.status(); return; } diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 677ddefb3..80ed9202a 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -12,6 +12,7 @@ #include +#include #include #include @@ -31,7 +32,8 @@ namespace if (val == "linear") return "Trilinear"; if (val == "nearest") return "Bilinear"; if (val != "none") - std::cerr<< "Warning: Invalid texture mipmap option: "< - #include - +#include #include #include #include @@ -245,7 +243,7 @@ namespace MWGui const ESM::Enchantment* ench = MWBase::Environment::get().getWorld()->getStore().get().search(enchId); if (!ench) { - std::cerr << "Warning: Can't find enchantment '" << enchId << "' on item " << base.getCellRef().getRefId() << std::endl; + Log(Debug::Warning) << "Warning: Can't find enchantment '" << enchId << "' on item " << base.getCellRef().getRefId(); return false; } diff --git a/apps/openmw/mwgui/spellmodel.cpp b/apps/openmw/mwgui/spellmodel.cpp index a2710b633..0933737ca 100644 --- a/apps/openmw/mwgui/spellmodel.cpp +++ b/apps/openmw/mwgui/spellmodel.cpp @@ -1,6 +1,6 @@ #include "spellmodel.hpp" -#include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -94,7 +94,7 @@ namespace MWGui const ESM::Enchantment* enchant = esmStore.get().search(enchantId); if (!enchant) { - std::cerr << "Warning: Can't find enchantment '" << enchantId << "' on item " << item.getCellRef().getRefId() << std::endl; + Log(Debug::Warning) << "Warning: Can't find enchantment '" << enchantId << "' on item " << item.getCellRef().getRefId(); continue; } diff --git a/apps/openmw/mwgui/videowidget.cpp b/apps/openmw/mwgui/videowidget.cpp index 5a4bb981f..516f5cfcc 100644 --- a/apps/openmw/mwgui/videowidget.cpp +++ b/apps/openmw/mwgui/videowidget.cpp @@ -6,6 +6,7 @@ #include +#include #include #include @@ -37,7 +38,7 @@ void VideoWidget::playVideo(const std::string &video) } catch (std::exception& e) { - std::cerr << "Failed to open video: " << e.what() << std::endl; + Log(Debug::Error) << "Failed to open video: " << e.what(); return; } diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index c97efed34..e1bf9627d 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -21,6 +21,8 @@ #include #include +#include + #include #include @@ -1082,7 +1084,7 @@ namespace MWGui { if (!mStore) { - std::cerr << "Error: WindowManager::onRetrieveTag: no Store set up yet, can not replace '" << tag << "'" << std::endl; + Log(Debug::Error) << "Error: WindowManager::onRetrieveTag: no Store set up yet, can not replace '" << tag << "'"; return; } const ESM::GameSetting *setting = mStore->get().find(tag); @@ -1788,7 +1790,7 @@ namespace MWGui if (found != mCurrentModals.end()) mCurrentModals.erase(found); else - std::cerr << " warning: can't find modal window " << input << std::endl; + Log(Debug::Warning) << "Warning: can't find modal window " << input; } } if (mCurrentModals.empty()) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 242058c5f..709c61196 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -10,6 +10,7 @@ #include +#include #include #include #include @@ -120,11 +121,11 @@ namespace MWInput SDL_ControllerDeviceEvent evt; evt.which = i; controllerAdded(mFakeDeviceID, evt); - std::cout << "Detected game controller: " << SDL_GameControllerNameForIndex(i) << std::endl; + Log(Debug::Info) << "Detected game controller: " << SDL_GameControllerNameForIndex(i); } else { - std::cout << "Detected unusable controller: " << SDL_JoystickNameForIndex(i) << std::endl; + Log(Debug::Info) << "Detected unusable controller: " << SDL_JoystickNameForIndex(i); } } diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 2d535f57b..d1f4e47df 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -8,7 +8,7 @@ #include #include - +#include #include #include "../mwworld/esmstore.hpp" @@ -1720,7 +1720,7 @@ namespace MWMechanics } else { - std::cerr<< "Warning: Actors::playAnimationGroup: Unable to find " << ptr.getCellRef().getRefId() << std::endl; + Log(Debug::Warning) << "Warning: Actors::playAnimationGroup: Unable to find " << ptr.getCellRef().getRefId(); return false; } } diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index 085174820..753dc240e 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -1,8 +1,8 @@ #include "aisequence.hpp" #include -#include +#include #include #include "../mwbase/environment.hpp" @@ -282,7 +282,7 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac } catch (std::exception& e) { - std::cerr << "Error during AiSequence::execute: " << e.what() << std::endl; + Log(Debug::Error) << "Error during AiSequence::execute: " << e.what(); } } } diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 928b09cf9..67468453a 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -1,10 +1,9 @@ #include "aiwander.hpp" #include -#include +#include #include - #include #include "../mwbase/world.hpp" @@ -24,8 +23,6 @@ #include "coordinateconverter.hpp" #include "actorutil.hpp" - - namespace MWMechanics { static const int COUNT_BEFORE_RESET = 10; @@ -677,7 +674,7 @@ namespace MWMechanics } else { - std::cerr<< "Error: Attempted to play out of range idle animation \""< - +#include #include #include "../mwworld/ptr.hpp" @@ -63,7 +62,7 @@ namespace MWMechanics // Vanilla doesn't fail on nonexistent items in levelled lists if (!MWBase::Environment::get().getWorld()->getStore().find(Misc::StringUtils::lowerCase(item))) { - std::cerr << "Warning: ignoring nonexistent item '" << item << "' in levelled list '" << levItem->mId << "'" << std::endl; + Log(Debug::Warning) << "Warning: ignoring nonexistent item '" << item << "' in levelled list '" << levItem->mId << "'"; return std::string(); } diff --git a/apps/openmw/mwmechanics/objects.cpp b/apps/openmw/mwmechanics/objects.cpp index 7d7a5f14f..d8821276e 100644 --- a/apps/openmw/mwmechanics/objects.cpp +++ b/apps/openmw/mwmechanics/objects.cpp @@ -1,6 +1,6 @@ #include "objects.hpp" -#include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -88,7 +88,7 @@ bool Objects::playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& gro } else { - std::cerr<< "Warning: Objects::playAnimationGroup: Unable to find " << ptr.getCellRef().getRefId() << std::endl; + Log(Debug::Warning) << "Warning: Objects::playAnimationGroup: Unable to find " << ptr.getCellRef().getRefId(); return false; } } diff --git a/apps/openmw/mwmechanics/summoning.cpp b/apps/openmw/mwmechanics/summoning.cpp index 71c49f9df..b3f7afc53 100644 --- a/apps/openmw/mwmechanics/summoning.cpp +++ b/apps/openmw/mwmechanics/summoning.cpp @@ -1,6 +1,6 @@ #include "summoning.hpp" -#include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -76,7 +76,7 @@ namespace MWMechanics } catch (std::exception& e) { - std::cerr << "Failed to spawn summoned creature: " << e.what() << std::endl; + Log(Debug::Error) << "Failed to spawn summoned creature: " << e.what(); // still insert into creatureMap so we don't try to spawn again every frame, that would spam the warning log } diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index ae8d76a80..13a0f23ac 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -1,6 +1,5 @@ #include "physicssystem.hpp" -#include #include #include @@ -20,7 +19,7 @@ #include #include #include - +#include #include #include #include @@ -638,7 +637,7 @@ namespace MWPhysics mPtr.getRefData().getBaseNode()->accept(visitor); if (!visitor.mFound) { - std::cerr << "Error: animateCollisionShapes can't find node " << recIndex << " for " << mPtr.getCellRef().getRefId() << std::endl; + Log(Debug::Warning) << "Warning: animateCollisionShapes can't find node " << recIndex << " for " << mPtr.getCellRef().getRefId(); // Remove nonexistent nodes from animated shapes map and early out mShapeInstance->mAnimatedShapes.erase(recIndex); @@ -708,7 +707,7 @@ namespace MWPhysics if (physFramerate > 0) { mPhysicsDt = 1.f / physFramerate; - std::cerr << "Warning: physics framerate was overridden (a new value is " << physFramerate << ")." << std::endl; + Log(Debug::Warning) << "Warning: using custom physics framerate (" << physFramerate << " FPS)."; } } } diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index ff31e1cfb..8e1105b9f 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -12,6 +12,8 @@ #include #include +#include + #include #include @@ -192,7 +194,7 @@ namespace for (RemoveVec::iterator it = mToRemove.begin(); it != mToRemove.end(); ++it) { if (!it->second->removeChild(it->first)) - std::cerr << "error removing " << it->first->getName() << std::endl; + Log(Debug::Error) << "Error removing " << it->first->getName(); } } @@ -612,7 +614,7 @@ namespace MWRender NodeMap::const_iterator found = nodeMap.find(bonename); if (found == nodeMap.end()) { - std::cerr << "Warning: addAnimSource: can't find bone '" + bonename << "' in " << baseModel << " (referenced by " << kfname << ")" << std::endl; + Log(Debug::Warning) << "Warning: addAnimSource: can't find bone '" + bonename << "' in " << baseModel << " (referenced by " << kfname << ")"; continue; } @@ -724,7 +726,7 @@ namespace MWRender } catch (std::exception& e) { - std::cerr << "Error handling text key " << evt << ": " << e.what() << std::endl; + Log(Debug::Error) << "Error handling text key " << evt << ": " << e.what(); } } } @@ -1767,12 +1769,12 @@ namespace MWRender PartHolder::~PartHolder() { if (mNode.get() && !mNode->getNumParents()) - std::cerr << "Error: part has no parents " << std::endl; + Log(Debug::Verbose) << "Part has no parents" ; if (mNode.get() && mNode->getNumParents()) { if (mNode->getNumParents() > 1) - std::cerr << "Error: part has multiple parents " << mNode->getNumParents() << " " << mNode.get() << std::endl; + Log(Debug::Verbose) << "Part has multiple (" << mNode->getNumParents() << ") parents"; mNode->getParent(0)->removeChild(mNode); } } diff --git a/apps/openmw/mwrender/bulletdebugdraw.cpp b/apps/openmw/mwrender/bulletdebugdraw.cpp index d35c3ac5d..eb3775cb4 100644 --- a/apps/openmw/mwrender/bulletdebugdraw.cpp +++ b/apps/openmw/mwrender/bulletdebugdraw.cpp @@ -1,12 +1,12 @@ #include "bulletdebugdraw.hpp" -#include - #include #include #include +#include + #include "vismask.hpp" namespace @@ -91,7 +91,7 @@ void DebugDrawer::drawContactPoint(const btVector3 &PointOnB, const btVector3 &n void DebugDrawer::reportErrorWarning(const char *warningString) { - std::cerr << warningString << std::endl; + Log(Debug::Warning) << warningString; } void DebugDrawer::setDebugMode(int isOn) diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index faaa3799e..2e0249e60 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -1,7 +1,6 @@ #include "characterpreview.hpp" #include -#include #include #include @@ -14,6 +13,7 @@ #include #include +#include #include #include @@ -474,7 +474,7 @@ namespace MWRender mCamera->addUpdateCallback(mUpdateCameraCallback); } else - std::cerr << "Error: Bip01 Head node not found" << std::endl; + Log(Debug::Error) << "Error: Bip01 Head node not found"; } } diff --git a/apps/openmw/mwrender/creatureanimation.cpp b/apps/openmw/mwrender/creatureanimation.cpp index 827b576c3..6db223bd5 100644 --- a/apps/openmw/mwrender/creatureanimation.cpp +++ b/apps/openmw/mwrender/creatureanimation.cpp @@ -1,11 +1,9 @@ #include "creatureanimation.hpp" -#include - #include #include - +#include #include #include #include @@ -155,7 +153,7 @@ void CreatureWeaponAnimation::updatePart(PartHolderPtr& scene, int slot) } catch (std::exception& e) { - std::cerr << "Can not add creature part: " << e.what() << std::endl; + Log(Debug::Error) << "Can not add creature part: " << e.what(); } } diff --git a/apps/openmw/mwrender/globalmap.cpp b/apps/openmw/mwrender/globalmap.cpp index af2bb101a..fae524faa 100644 --- a/apps/openmw/mwrender/globalmap.cpp +++ b/apps/openmw/mwrender/globalmap.cpp @@ -15,6 +15,8 @@ #include #include +#include + #include #include @@ -411,14 +413,14 @@ namespace MWRender osgDB::ReaderWriter* readerwriter = osgDB::Registry::instance()->getReaderWriterForExtension("png"); if (!readerwriter) { - std::cerr << "Error: Can't write map overlay: no png readerwriter found" << std::endl; + Log(Debug::Error) << "Error: Can't write map overlay: no png readerwriter found"; return; } osgDB::ReaderWriter::WriteResult result = readerwriter->writeImage(*mOverlayImage, ostream); if (!result.success()) { - std::cerr << "Error: Can't write map overlay: " << result.message() << " code " << result.status() << std::endl; + Log(Debug::Warning) << "Error: Can't write map overlay: " << result.message() << " code " << result.status(); return; } @@ -463,14 +465,14 @@ namespace MWRender osgDB::ReaderWriter* readerwriter = osgDB::Registry::instance()->getReaderWriterForExtension("png"); if (!readerwriter) { - std::cerr << "Error: Can't read map overlay: no png readerwriter found" << std::endl; + Log(Debug::Error) << "Error: Can't read map overlay: no png readerwriter found"; return; } osgDB::ReaderWriter::ReadResult result = readerwriter->readImage(istream); if (!result.success()) { - std::cerr << "Error: Can't read map overlay: " << result.message() << " code " << result.status() << std::endl; + Log(Debug::Error) << "Error: Can't read map overlay: " << result.message() << " code " << result.status(); return; } @@ -572,7 +574,7 @@ namespace MWRender CameraVector::iterator found = std::find(mActiveCameras.begin(), mActiveCameras.end(), camera); if (found == mActiveCameras.end()) { - std::cerr << "Error: GlobalMap trying to remove an inactive camera" << std::endl; + Log(Debug::Error) << "Error: GlobalMap trying to remove an inactive camera"; return; } mActiveCameras.erase(found); diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index 0b65a6b13..5e501ecf8 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -1,6 +1,5 @@ #include "localmap.hpp" -#include #include #include @@ -12,6 +11,7 @@ #include +#include #include #include #include @@ -320,7 +320,7 @@ void LocalMap::markForRemoval(osg::Camera *cam) CameraVector::iterator found = std::find(mActiveCameras.begin(), mActiveCameras.end(), cam); if (found == mActiveCameras.end()) { - std::cerr << "Error: trying to remove an inactive camera" << std::endl; + Log(Debug::Error) << "Error: trying to remove an inactive camera"; return; } mActiveCameras.erase(found); @@ -492,7 +492,7 @@ void LocalMap::requestInteriorMap(const MWWorld::CellStore* cell) // We are using the same bounds and angle as we were using when the textures were originally made. Segments should come out the same. if (i >= int(fog->mFogTextures.size())) { - std::cout << "Error: fog texture count mismatch" << std::endl; + Log(Debug::Warning) << "Warning: fog texture count mismatch"; break; } @@ -684,7 +684,7 @@ void LocalMap::MapSegment::loadFogOfWar(const ESM::FogTexture &esm) osgDB::ReaderWriter* readerwriter = osgDB::Registry::instance()->getReaderWriterForExtension("tga"); if (!readerwriter) { - std::cerr << "Error: Unable to load fog, can't find a tga ReaderWriter" << std::endl; + Log(Debug::Error) << "Error: Unable to load fog, can't find a tga ReaderWriter" ; return; } @@ -693,7 +693,7 @@ void LocalMap::MapSegment::loadFogOfWar(const ESM::FogTexture &esm) osgDB::ReaderWriter::ReadResult result = readerwriter->readImage(in); if (!result.success()) { - std::cerr << "Error: Failed to read fog: " << result.message() << " code " << result.status() << std::endl; + Log(Debug::Error) << "Error: Failed to read fog: " << result.message() << " code " << result.status(); return; } @@ -716,7 +716,7 @@ void LocalMap::MapSegment::saveFogOfWar(ESM::FogTexture &fog) const osgDB::ReaderWriter* readerwriter = osgDB::Registry::instance()->getReaderWriterForExtension("tga"); if (!readerwriter) { - std::cerr << "Error: Unable to write fog, can't find a tga ReaderWriter" << std::endl; + Log(Debug::Error) << "Error: Unable to write fog, can't find a tga ReaderWriter"; return; } @@ -725,7 +725,7 @@ void LocalMap::MapSegment::saveFogOfWar(ESM::FogTexture &fog) const osgDB::ReaderWriter::WriteResult result = readerwriter->writeImage(*mFogOfWarImage, ostream); if (!result.success()) { - std::cerr << "Error: Unable to write fog: " << result.message() << " code " << result.status() << std::endl; + Log(Debug::Error) << "Error: Unable to write fog: " << result.message() << " code " << result.status(); return; } mFogOfWarImage->flipVertical(); diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index baf6cb822..98f8ce892 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -7,6 +7,8 @@ #include #include +#include + #include #include @@ -435,7 +437,7 @@ void NpcAnimation::updateNpcBase() if (bp) mHeadModel = "meshes\\" + bp->mModel; else - std::cerr << "Warning: Failed to load body part '" << mNpc->mHead << "'" << std::endl; + Log(Debug::Warning) << "Warning: Failed to load body part '" << mNpc->mHead << "'"; } mHairModel = ""; @@ -445,7 +447,7 @@ void NpcAnimation::updateNpcBase() if (bp) mHairModel = "meshes\\" + bp->mModel; else - std::cerr << "Warning: Failed to load body part '" << mNpc->mHair << "'" << std::endl; + Log(Debug::Warning) << "Warning: Failed to load body part '" << mNpc->mHair << "'"; } } @@ -758,7 +760,7 @@ bool NpcAnimation::addOrReplaceIndividualPart(ESM::PartReferenceType type, int g } catch (std::exception& e) { - std::cerr << "Error adding NPC part: " << e.what() << std::endl; + Log(Debug::Error) << "Error adding NPC part: " << e.what(); return false; } @@ -845,7 +847,7 @@ void NpcAnimation::addPartGroup(int group, int priority, const std::vectormFemale << "'" << std::endl; + Log(Debug::Warning) << "Warning: Failed to find body part '" << part->mFemale << "'"; } if(!bodypart && !part->mMale.empty()) { @@ -860,7 +862,7 @@ void NpcAnimation::addPartGroup(int group, int priority, const std::vectormMale << "'" << std::endl; + Log(Debug::Warning) << "Warning: Failed to find body part '" << part->mMale << "'"; } if(bodypart) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 03863000f..e17fe3191 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -22,6 +22,8 @@ #include +#include + #include #include #include @@ -709,7 +711,7 @@ namespace MWRender if (!found) { - std::cerr << "Wrong screenshot type: " << settingArgs[0] << "." << std::endl; + Log(Debug::Warning) << "Wrong screenshot type: " << settingArgs[0] << "."; return false; } } @@ -728,7 +730,7 @@ namespace MWRender if (mCamera->isVanityOrPreviewModeEnabled()) { - std::cerr << "Spherical screenshots are not allowed in preview mode." << std::endl; + Log(Debug::Warning) << "Spherical screenshots are not allowed in preview mode."; return false; } diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index 3c617f794..52832ad87 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -19,6 +19,8 @@ #include #include +#include + #include #include #include @@ -29,7 +31,6 @@ #include - #include #include @@ -193,16 +194,16 @@ osg::ref_ptr readPngImage (const std::string& file) boost::filesystem::ifstream inStream; inStream.open(file, std::ios_base::in | std::ios_base::binary); if (inStream.fail()) - std::cerr << "Error: Failed to open " << file << std::endl; + Log(Debug::Error) << "Error: Failed to open " << file; osgDB::ReaderWriter* reader = osgDB::Registry::instance()->getReaderWriterForExtension("png"); if (!reader) { - std::cerr << "Error: Failed to read " << file << ", no png readerwriter found" << std::endl; + Log(Debug::Error) << "Error: Failed to read " << file << ", no png readerwriter found"; return osg::ref_ptr(); } osgDB::ReaderWriter::ReadResult result = reader->readImage(inStream); if (!result.success()) - std::cerr << "Error: Failed to read " << file << ": " << result.message() << " code " << result.status() << std::endl; + Log(Debug::Error) << "Error: Failed to read " << file << ": " << result.message() << " code " << result.status(); return result.getImage(); } diff --git a/apps/openmw/mwscript/aiextensions.cpp b/apps/openmw/mwscript/aiextensions.cpp index c51a55b50..0bb94a134 100644 --- a/apps/openmw/mwscript/aiextensions.cpp +++ b/apps/openmw/mwscript/aiextensions.cpp @@ -1,7 +1,8 @@ #include "aiextensions.hpp" #include -#include + +#include #include #include @@ -50,7 +51,7 @@ namespace MWScript MWMechanics::AiActivate activatePackage(objectID); ptr.getClass().getCreatureStats (ptr).getAiSequence().stack(activatePackage, ptr); - std::cout << "AiActivate" << std::endl; + Log(Debug::Info) << "AiActivate"; } }; @@ -78,7 +79,7 @@ namespace MWScript MWMechanics::AiTravel travelPackage(x, y, z); ptr.getClass().getCreatureStats (ptr).getAiSequence().stack(travelPackage, ptr); - std::cout << "AiTravel: " << x << ", " << y << ", " << z << std::endl; + Log(Debug::Info) << "AiTravel: " << x << ", " << y << ", " << z; } }; @@ -112,8 +113,7 @@ namespace MWScript MWMechanics::AiEscort escortPackage(actorID, static_cast(duration), x, y, z); ptr.getClass().getCreatureStats (ptr).getAiSequence().stack(escortPackage, ptr); - std::cout << "AiEscort: " << x << ", " << y << ", " << z << ", " << duration - << std::endl; + Log(Debug::Info) << "AiEscort: " << x << ", " << y << ", " << z << ", " << duration; } }; @@ -155,8 +155,7 @@ namespace MWScript MWMechanics::AiEscort escortPackage(actorID, cellID, static_cast(duration), x, y, z); ptr.getClass().getCreatureStats (ptr).getAiSequence().stack(escortPackage, ptr); - std::cout << "AiEscort: " << x << ", " << y << ", " << z << ", " << duration - << std::endl; + Log(Debug::Info) << "AiEscort: " << x << ", " << y << ", " << z << ", " << duration; } }; @@ -316,8 +315,7 @@ namespace MWScript MWMechanics::AiFollow followPackage(actorID, duration, x, y ,z); ptr.getClass().getCreatureStats (ptr).getAiSequence().stack(followPackage, ptr); - std::cout << "AiFollow: " << actorID << ", " << x << ", " << y << ", " << z << ", " << duration - << std::endl; + Log(Debug::Info) << "AiFollow: " << actorID << ", " << x << ", " << y << ", " << z << ", " << duration; } }; @@ -353,8 +351,7 @@ namespace MWScript MWMechanics::AiFollow followPackage(actorID, cellID, duration, x, y ,z); ptr.getClass().getCreatureStats (ptr).getAiSequence().stack(followPackage, ptr); - std::cout << "AiFollow: " << actorID << ", " << x << ", " << y << ", " << z << ", " << duration - << std::endl; + Log(Debug::Info) << "AiFollow: " << actorID << ", " << x << ", " << y << ", " << z << ", " << duration; } }; diff --git a/apps/openmw/mwscript/containerextensions.cpp b/apps/openmw/mwscript/containerextensions.cpp index 0feadaa59..dd3e54a88 100644 --- a/apps/openmw/mwscript/containerextensions.cpp +++ b/apps/openmw/mwscript/containerextensions.cpp @@ -6,6 +6,8 @@ #include +#include + #include #include @@ -190,8 +192,8 @@ namespace MWScript if (it == invStore.end()) { it = ptr.getClass().getContainerStore (ptr).add (item, 1, ptr); - std::cerr << "Implicitly adding one " << item << " to container " - "to fulfil requirements of Equip instruction" << std::endl; + Log(Debug::Warning) << "Implicitly adding one " << item << " to container " + "to fulfil requirements of Equip instruction"; } if (ptr == MWMechanics::getPlayer()) diff --git a/apps/openmw/mwscript/dialogueextensions.cpp b/apps/openmw/mwscript/dialogueextensions.cpp index 4b6ddcf9f..135cb787e 100644 --- a/apps/openmw/mwscript/dialogueextensions.cpp +++ b/apps/openmw/mwscript/dialogueextensions.cpp @@ -1,10 +1,8 @@ -#include - #include "dialogueextensions.hpp" #include #include - +#include #include #include #include @@ -139,8 +137,8 @@ namespace MWScript if (!ptr.getClass().isActor()) { const std::string error = "Warning: \"forcegreeting\" command works only for actors."; - runtime.getContext().report (error); - std::cerr << error << std::endl; + runtime.getContext().report(error); + Log(Debug::Warning) << error; return; } diff --git a/apps/openmw/mwscript/globalscripts.cpp b/apps/openmw/mwscript/globalscripts.cpp index 6074a12d0..a52a4938a 100644 --- a/apps/openmw/mwscript/globalscripts.cpp +++ b/apps/openmw/mwscript/globalscripts.cpp @@ -1,8 +1,8 @@ #include "globalscripts.hpp" #include -#include +#include #include #include #include @@ -113,9 +113,9 @@ namespace MWScript } catch (const std::exception& exception) { - std::cerr + Log(Debug::Error) << "Failed to add start script " << *iter << " because an exception has " - << "been thrown: " << exception.what() << std::endl; + << "been thrown: " << exception.what(); } } } @@ -169,10 +169,9 @@ namespace MWScript } catch (const std::exception& exception) { - std::cerr + Log(Debug::Error) << "Failed to add start script " << script.mId - << " because an exception has been thrown: " << exception.what() - << std::endl; + << " because an exception has been thrown: " << exception.what(); return true; } diff --git a/apps/openmw/mwscript/locals.cpp b/apps/openmw/mwscript/locals.cpp index 64f3058eb..4c5573168 100644 --- a/apps/openmw/mwscript/locals.cpp +++ b/apps/openmw/mwscript/locals.cpp @@ -3,7 +3,7 @@ #include #include #include - +#include #include #include @@ -229,10 +229,10 @@ namespace MWScript } catch (std::exception& e) { - std::cerr << "Failed to read local variable state for script '" - << script << "' (legacy format): " << e.what() - << "\nNum shorts: " << numshorts << " / " << mShorts.size() - << " Num longs: " << numlongs << " / " << mLongs.size() << std::endl; + Log(Debug::Error) << "Failed to read local variable state for script '" + << script << "' (legacy format): " << e.what() + << "\nNum shorts: " << numshorts << " / " << mShorts.size() + << " Num longs: " << numlongs << " / " << mLongs.size(); } } else diff --git a/apps/openmw/mwscript/scriptmanagerimp.cpp b/apps/openmw/mwscript/scriptmanagerimp.cpp index 7c1f9bf4d..80496d6db 100644 --- a/apps/openmw/mwscript/scriptmanagerimp.cpp +++ b/apps/openmw/mwscript/scriptmanagerimp.cpp @@ -1,11 +1,12 @@ #include "scriptmanagerimp.hpp" #include -#include #include #include #include +#include + #include #include @@ -65,14 +66,14 @@ namespace MWScript } catch (const std::exception& error) { - std::cerr << "Error: An exception has been thrown: " << error.what() << std::endl; + Log(Debug::Error) << "Error: An exception has been thrown: " << error.what(); Success = false; } if (!Success) { - std::cerr - << "Warning: compiling failed: " << name << std::endl; + Log(Debug::Warning) + << "Warning: compiling failed: " << name; } if (Success) @@ -121,8 +122,8 @@ namespace MWScript } catch (const std::exception& e) { - std::cerr << "Execution of script " << name << " failed:" << std::endl; - std::cerr << e.what() << std::endl; + Log(Debug::Error) << "Execution of script " << name << " failed:"; + Log(Debug::Error) << e.what(); iter->second.first.clear(); // don't execute again. } diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index fadc99b66..2ff5035ae 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -1,6 +1,5 @@ #include "statsextensions.hpp" -#include #include #include @@ -9,7 +8,7 @@ #include #include - +#include #include #include #include @@ -246,9 +245,10 @@ namespace MWScript if (R()(runtime, false, true).isEmpty()) { - std::cerr + Log(Debug::Warning) << "Warning: Compensating for broken script in Morrowind.esm by " - << "ignoring remote access to dagoth_ur_1" << std::endl; + << "ignoring remote access to dagoth_ur_1"; + return; } } diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index 611199f72..8f0c72519 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -1,4 +1,4 @@ -#include +#include #include @@ -317,7 +317,7 @@ namespace MWScript { std::string error = "Warning: PositionCell: unknown interior cell (" + cellID + "), moving to exterior instead"; runtime.getContext().report (error); - std::cerr << error << std::endl; + Log(Debug::Warning) << error; } } if(store) @@ -429,7 +429,7 @@ namespace MWScript if(!cell) { runtime.getContext().report ("unknown cell (" + cellID + ")"); - std::cerr << "unknown cell (" << cellID << ")\n"; + Log(Debug::Error) << "Error: unknown cell (" << cellID << ")"; } } if(store) diff --git a/apps/openmw/mwsound/ffmpeg_decoder.cpp b/apps/openmw/mwsound/ffmpeg_decoder.cpp index f458c0a97..7dffd685a 100644 --- a/apps/openmw/mwsound/ffmpeg_decoder.cpp +++ b/apps/openmw/mwsound/ffmpeg_decoder.cpp @@ -3,9 +3,9 @@ #include #include -#include #include +#include #include namespace MWSound @@ -28,7 +28,7 @@ int FFmpeg_Decoder::readPacket(void *user_data, uint8_t *buf, int buf_size) int FFmpeg_Decoder::writePacket(void *, uint8_t *, int) { - std::cerr<< "can't write to read-only stream" <codec->channels, (*mStream)->codec->channel_layout); - std::cerr<< "Unsupported channel layout: "<codec->channels == 1) { @@ -385,7 +385,7 @@ size_t FFmpeg_Decoder::read(char *buffer, size_t bytes) { if(!mStream) { - std::cerr<< "No audio stream" < &output) { if(!mStream) { - std::cerr<< "No audio stream" < #include -#include #include #include #include @@ -8,6 +7,7 @@ #include +#include #include #include @@ -44,8 +44,7 @@ ALCenum checkALCError(ALCdevice *device, const char *func, int line) { ALCenum err = alcGetError(device); if(err != ALC_NO_ERROR) - std::cerr<< ">>>>>>>>> ALC error "<>>>>>>>> AL error "<getInfo(&mSampleRate, &chans, &type); mFormat = getALFormat(chans, type); } - catch(std::exception &e) { - std::cerr<< "Failed to get stream info: "<getName()<<"\"" <getName() << "\""; mIsFinished = true; } return !mIsFinished; @@ -593,17 +591,18 @@ bool OpenAL_Output::init(const std::string &devname, const std::string &hrtfname { deinit(); - std::cout<< "Initializing OpenAL..." < OpenAL_Output::loadSound(const std::string &fname } catch(std::exception &e) { - std::cerr<< "Failed to load audio from "<getIsLooping()) - std::cout <<"Warning: cannot loop stream \""<getName()<<"\""<< std::endl; + Log(Debug::Warning) << "Warning: cannot loop stream \"" << decoder->getName() << "\""; + initCommon2D(source, sound->getPosition(), sound->getRealVolume(), sound->getPitch(), false, sound->getUseEnv()); if(getALError() != AL_NO_ERROR) @@ -1266,13 +1266,14 @@ bool OpenAL_Output::streamSound3D(DecoderPtr decoder, Stream *sound, bool getLou { if(mFreeSources.empty()) { - std::cerr<< "No free sources!" <getIsLooping()) - std::cout <<"Warning: cannot loop stream \""<getName()<<"\""<< std::endl; + Log(Debug::Warning) << "Warning: cannot loop stream \"" << decoder->getName() << "\""; + initCommon3D(source, sound->getPosition(), sound->getMinDistance(), sound->getMaxDistance(), sound->getRealVolume(), sound->getPitch(), false, sound->getUseEnv()); if(getALError() != AL_NO_ERROR) diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 8c71ab61b..d6dc2ba99 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -1,6 +1,5 @@ #include "soundmanagerimp.hpp" -#include #include #include #include @@ -8,7 +7,7 @@ #include #include - +#include #include #include "../mwbase/environment.hpp" @@ -81,7 +80,7 @@ namespace MWSound if(!useSound) { - std::cout<< "Sound disabled." <init(devname, hrtfname, hrtfmode)) { - std::cerr<< "Failed to initialize audio output, sound disabled" < names = mOutput->enumerate(); - std::cout <<"Enumerated output devices:\n"; + std::stringstream stream; + + stream << "Enumerated output devices:\n"; for(const std::string &name : names) - std::cout <<" "<enumerateHrtf(); if(!names.empty()) { - std::cout<< "Enumerated HRTF names:\n"; + stream << "Enumerated HRTF names:\n"; for(const std::string &name : names) - std::cout <<" "<isInitialized()) return; - std::cout <<"Playing "< + #include #include #include @@ -157,7 +159,7 @@ void MWState::StateManager::newGame (bool bypass) std::stringstream error; error << "Failed to start new game: " << e.what(); - std::cerr << error.str() << std::endl; + Log(Debug::Error) << error.str(); cleanup (true); MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); @@ -283,7 +285,7 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot // Ensure we have written the number of records that was estimated if (writer.getRecordCount() != recordCount+1) // 1 extra for TES3 record - std::cerr << "Warning: number of written savegame records does not match. Estimated: " << recordCount+1 << ", written: " << writer.getRecordCount() << std::endl; + Log(Debug::Warning) << "Warning: number of written savegame records does not match. Estimated: " << recordCount+1 << ", written: " << writer.getRecordCount(); writer.close(); @@ -305,7 +307,7 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot std::stringstream error; error << "Failed to save game: " << e.what(); - std::cerr << error.str() << std::endl; + Log(Debug::Error) << error.str(); std::vector buttons; buttons.push_back("#{sOk}"); @@ -483,7 +485,7 @@ void MWState::StateManager::loadGame (const Character *character, const std::str default: // ignore invalid records - std::cerr << "Warning: Ignoring unknown record: " << n.toString() << std::endl; + Log(Debug::Warning) << "Warning: Ignoring unknown record: " << n.toString(); reader.skipRecord(); } int progressPercent = static_cast(float(reader.getFileOffset())/total*100); @@ -549,7 +551,7 @@ void MWState::StateManager::loadGame (const Character *character, const std::str std::stringstream error; error << "Failed to load saved game: " << e.what(); - std::cerr << error.str() << std::endl; + Log(Debug::Error) << error.str(); cleanup (true); MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); @@ -625,7 +627,7 @@ bool MWState::StateManager::verifyProfile(const ESM::SavedGame& profile) const if (std::find(selectedContentFiles.begin(), selectedContentFiles.end(), *it) == selectedContentFiles.end()) { - std::cerr << "Warning: Savegame dependency " << *it << " is missing." << std::endl; + Log(Debug::Warning) << "Warning: Savegame dependency " << *it << " is missing."; notFound = true; } } @@ -653,7 +655,7 @@ void MWState::StateManager::writeScreenshot(std::vector &imageData) const osgDB::ReaderWriter* readerwriter = osgDB::Registry::instance()->getReaderWriterForExtension("jpg"); if (!readerwriter) { - std::cerr << "Error: Unable to write screenshot, can't find a jpg ReaderWriter" << std::endl; + Log(Debug::Error) << "Error: Unable to write screenshot, can't find a jpg ReaderWriter"; return; } @@ -661,7 +663,7 @@ void MWState::StateManager::writeScreenshot(std::vector &imageData) const osgDB::ReaderWriter::WriteResult result = readerwriter->writeImage(*screenshot, ostream); if (!result.success()) { - std::cerr << "Error: Unable to write screenshot: " << result.message() << " code " << result.status() << std::endl; + Log(Debug::Error) << "Error: Unable to write screenshot: " << result.message() << " code " << result.status(); return; } diff --git a/apps/openmw/mwworld/cellpreloader.cpp b/apps/openmw/mwworld/cellpreloader.cpp index 700b91b37..9163f1f2e 100644 --- a/apps/openmw/mwworld/cellpreloader.cpp +++ b/apps/openmw/mwworld/cellpreloader.cpp @@ -1,7 +1,6 @@ #include "cellpreloader.hpp" -#include - +#include #include #include #include @@ -229,12 +228,12 @@ namespace MWWorld { if (!mWorkQueue) { - std::cerr << "Error: can't preload, no work queue set " << std::endl; + Log(Debug::Error) << "Error: can't preload, no work queue set"; return; } if (cell->getState() == CellStore::State_Unloaded) { - std::cerr << "Error: can't preload objects for unloaded cell" << std::endl; + Log(Debug::Error) << "Error: can't preload objects for unloaded cell"; return; } diff --git a/apps/openmw/mwworld/cells.cpp b/apps/openmw/mwworld/cells.cpp index 752659eb6..5cac12b9c 100644 --- a/apps/openmw/mwworld/cells.cpp +++ b/apps/openmw/mwworld/cells.cpp @@ -1,7 +1,6 @@ #include "cells.hpp" -#include - +#include #include #include #include @@ -350,7 +349,7 @@ bool MWWorld::Cells::readRecord (ESM::ESMReader& reader, uint32_t type, catch (...) { // silently drop cells that don't exist anymore - std::cerr << "Warning: Dropping state for cell " << state.mId.mWorldspace << " (cell no longer exists)" << std::endl; + Log(Debug::Warning) << "Warning: Dropping state for cell " << state.mId.mWorldspace << " (cell no longer exists)"; reader.skipRecord(); return true; } diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index fc3c2e245..40e904ec2 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -1,8 +1,9 @@ #include "cellstore.hpp" -#include #include +#include + #include #include #include @@ -138,7 +139,7 @@ namespace return; } - std::cerr << "Warning: Dropping reference to " << state.mRef.mRefID << " (invalid content file link)" << std::endl; + Log(Debug::Warning) << "Warning: Dropping reference to " << state.mRef.mRefID << " (invalid content file link)"; return; } @@ -196,9 +197,9 @@ namespace MWWorld } else { - std::cerr + Log(Debug::Warning) << "Warning: could not resolve cell reference '" << ref.mRefID << "'" - << " (dropping reference)" << std::endl; + << " (dropping reference)"; } } @@ -497,7 +498,7 @@ namespace MWWorld } catch (std::exception& e) { - std::cerr << "An error occurred listing references for cell " << getCell()->getDescription() << ": " << e.what() << std::endl; + Log(Debug::Error) << "An error occurred listing references for cell " << getCell()->getDescription() << ": " << e.what(); } } @@ -553,7 +554,7 @@ namespace MWWorld } catch (std::exception& e) { - std::cerr << "An error occurred loading references for cell " << getCell()->getDescription() << ": " << e.what() << std::endl; + Log(Debug::Error) << "An error occurred loading references for cell " << getCell()->getDescription() << ": " << e.what(); } } @@ -659,11 +660,10 @@ namespace MWWorld case ESM::REC_WEAP: mWeapons.load(ref, deleted, store); break; case ESM::REC_BODY: mBodyParts.load(ref, deleted, store); break; - case 0: std::cerr << "Cell reference '" + ref.mRefID + "' not found!\n"; return; + case 0: Log(Debug::Error) << "Cell reference '" + ref.mRefID + "' not found!"; return; default: - std::cerr - << "Error: Ignoring reference '" << ref.mRefID << "' of unhandled type\n"; + Log(Debug::Error) << "Error: Ignoring reference '" << ref.mRefID << "' of unhandled type"; return; } @@ -756,7 +756,7 @@ namespace MWWorld int type = MWBase::Environment::get().getWorld()->getStore().find(cref.mRefID); if (type == 0) { - std::cerr << "Dropping reference to '" << cref.mRefID << "' (object no longer exists)" << std::endl; + Log(Debug::Warning) << "Dropping reference to '" << cref.mRefID << "' (object no longer exists)"; reader.skipHSubUntil("OBJE"); continue; } @@ -892,7 +892,7 @@ namespace MWWorld if (!visitor.mFound) { - std::cerr << "Warning: Dropping moved ref tag for " << refnum.mIndex << " (moved object no longer exists)" << std::endl; + Log(Debug::Warning) << "Warning: Dropping moved ref tag for " << refnum.mIndex << " (moved object no longer exists)"; continue; } @@ -902,8 +902,8 @@ namespace MWWorld if (otherCell == NULL) { - std::cerr << "Warning: Dropping moved ref tag for " << movedRef->mRef.getRefId() - << " (target cell " << movedTo.mWorldspace << " no longer exists). Reference moved back to its original location." << std::endl; + Log(Debug::Warning) << "Warning: Dropping moved ref tag for " << movedRef->mRef.getRefId() + << " (target cell " << movedTo.mWorldspace << " no longer exists). Reference moved back to its original location."; // Note by dropping tag the object will automatically re-appear in its original cell, though potentially at inapproriate coordinates. // Restore original coordinates: movedRef->mData.setPosition(movedRef->mRef.getPosition()); @@ -913,7 +913,7 @@ namespace MWWorld if (otherCell == this) { // Should never happen unless someone's tampering with files. - std::cerr << "Found invalid moved ref, ignoring" << std::endl; + Log(Debug::Warning) << "Found invalid moved ref, ignoring"; continue; } diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 6c369a154..8fa16ff51 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include "../mwbase/environment.hpp" @@ -502,7 +503,7 @@ void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std:: } catch (const std::exception& e) { - std::cerr << "Warning: MWWorld::ContainerStore::addInitialItem: " << e.what() << std::endl; + Log(Debug::Warning) << "Warning: MWWorld::ContainerStore::addInitialItem: " << e.what(); } } @@ -826,10 +827,10 @@ void MWWorld::ContainerStore::readState (const ESM::InventoryState& inventory) case ESM::REC_WEAP: readEquipmentState (getState (weapons, state), thisIndex, inventory); break; case ESM::REC_LIGH: readEquipmentState (getState (lights, state), thisIndex, inventory); break; case 0: - std::cerr << "Dropping inventory reference to '" << state.mRef.mRefID << "' (object no longer exists)" << std::endl; + Log(Debug::Warning) << "Dropping inventory reference to '" << state.mRef.mRefID << "' (object no longer exists)"; break; default: - std::cerr << "Warning: Invalid item type in inventory state, refid " << state.mRef.mRefID << std::endl; + Log(Debug::Warning) << "Warning: Invalid item type in inventory state, refid " << state.mRef.mRefID; break; } } diff --git a/apps/openmw/mwworld/contentloader.hpp b/apps/openmw/mwworld/contentloader.hpp index 0f2d807aa..2069a78fc 100644 --- a/apps/openmw/mwworld/contentloader.hpp +++ b/apps/openmw/mwworld/contentloader.hpp @@ -2,10 +2,10 @@ #define CONTENTLOADER_HPP #include -#include #include #include +#include #include "components/loadinglistener/loadinglistener.hpp" namespace MWWorld @@ -24,8 +24,8 @@ struct ContentLoader virtual void load(const boost::filesystem::path& filepath, int& index) { - std::cout << "Loading content file " << filepath.string() << std::endl; - mListener.setLabel(MyGUI::TextIterator::toTagsString(filepath.string())); + Log(Debug::Info) << "Loading content file " << filepath.string(); + mListener.setLabel(MyGUI::TextIterator::toTagsString(filepath.string())); } protected: diff --git a/apps/openmw/mwworld/esmstore.cpp b/apps/openmw/mwworld/esmstore.cpp index ac5608b8e..01d8e4b82 100644 --- a/apps/openmw/mwworld/esmstore.cpp +++ b/apps/openmw/mwworld/esmstore.cpp @@ -1,12 +1,11 @@ #include "esmstore.hpp" #include -#include #include +#include #include - #include #include @@ -84,7 +83,7 @@ void ESMStore::load(ESM::ESMReader &esm, Loading::Listener* listener) } else { - std::cerr << "error: info record without dialog" << std::endl; + Log(Debug::Error) << "Error: info record without dialog"; esm.skipRecord(); } } else if (n.intval == ESM::REC_MGEF) { @@ -170,7 +169,7 @@ void ESMStore::validate() const ESM::Faction *fact = mFactions.search(npcFaction); if (!fact) { - std::cerr << "NPC '" << npc.mId << "' (" << npc.mName << ") has nonexistent faction '" << npc.mFaction << "', ignoring it." << std::endl; + Log(Debug::Verbose) << "NPC '" << npc.mId << "' (" << npc.mName << ") has nonexistent faction '" << npc.mFaction << "', ignoring it."; npc.mFaction = ""; npc.mNpdt.mRank = -1; changed = true; @@ -183,7 +182,7 @@ void ESMStore::validate() const ESM::Class *cls = mClasses.search(npcClass); if (!cls) { - std::cerr << "NPC '" << npc.mId << "' (" << npc.mName << ") has nonexistent class '" << npc.mClass << "', using '" << defaultCls << "' class as replacement." << std::endl; + Log(Debug::Verbose) << "NPC '" << npc.mId << "' (" << npc.mName << ") has nonexistent class '" << npc.mClass << "', using '" << defaultCls << "' class as replacement."; npc.mClass = defaultCls; changed = true; } diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index ba3ea2381..6cfffcde6 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -895,7 +896,7 @@ void MWWorld::InventoryStore::updateRechargingItems() enchantmentId); if (!enchantment) { - std::cerr << "Warning: Can't find enchantment '" << enchantmentId << "' on item " << it->getCellRef().getRefId() << std::endl; + Log(Debug::Warning) << "Warning: Can't find enchantment '" << enchantmentId << "' on item " << it->getCellRef().getRefId(); continue; } diff --git a/apps/openmw/mwworld/livecellref.cpp b/apps/openmw/mwworld/livecellref.cpp index b8178f774..9cf8a0fe0 100644 --- a/apps/openmw/mwworld/livecellref.cpp +++ b/apps/openmw/mwworld/livecellref.cpp @@ -1,7 +1,6 @@ #include "livecellref.hpp" -#include - +#include #include #include "../mwbase/environment.hpp" @@ -38,10 +37,9 @@ void MWWorld::LiveCellRefBase::loadImp (const ESM::ObjectState& state) } catch (const std::exception& exception) { - std::cerr + Log(Debug::Error) << "Error: failed to load state for local script " << scriptId - << " because an exception has been thrown: " << exception.what() - << std::endl; + << " because an exception has been thrown: " << exception.what(); } } } @@ -51,7 +49,7 @@ void MWWorld::LiveCellRefBase::loadImp (const ESM::ObjectState& state) if (!mRef.getSoul().empty() && !MWBase::Environment::get().getWorld()->getStore().get().search(mRef.getSoul())) { - std::cerr << "Soul '" << mRef.getSoul() << "' not found, removing the soul from soul gem" << std::endl; + Log(Debug::Warning) << "Soul '" << mRef.getSoul() << "' not found, removing the soul from soul gem"; mRef.setSoul(std::string()); } } diff --git a/apps/openmw/mwworld/localscripts.cpp b/apps/openmw/mwworld/localscripts.cpp index 4ed83bf11..ff47d3e56 100644 --- a/apps/openmw/mwworld/localscripts.cpp +++ b/apps/openmw/mwworld/localscripts.cpp @@ -1,14 +1,12 @@ #include "localscripts.hpp" -#include +#include #include "esmstore.hpp" #include "cellstore.hpp" - #include "class.hpp" #include "containerstore.hpp" - namespace { @@ -93,7 +91,7 @@ void MWWorld::LocalScripts::add (const std::string& scriptName, const Ptr& ptr) for (std::list >::iterator iter = mScripts.begin(); iter!=mScripts.end(); ++iter) if (iter->second==ptr) { - std::cerr << "Error: tried to add local script twice for " << ptr.getCellRef().getRefId() << std::endl; + Log(Debug::Warning) << "Error: tried to add local script twice for " << ptr.getCellRef().getRefId(); remove(ptr); break; } @@ -102,15 +100,15 @@ void MWWorld::LocalScripts::add (const std::string& scriptName, const Ptr& ptr) } catch (const std::exception& exception) { - std::cerr + Log(Debug::Error) << "failed to add local script " << scriptName - << " because an exception has been thrown: " << exception.what() << std::endl; + << " because an exception has been thrown: " << exception.what(); } } else - std::cerr + Log(Debug::Warning) << "failed to add local script " << scriptName - << " because the script does not exist." << std::endl; + << " because the script does not exist."; } void MWWorld::LocalScripts::addCell (CellStore *cell) diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index 5439447fd..44b78336d 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -1,7 +1,8 @@ #include "player.hpp" #include -#include + +#include #include #include @@ -364,7 +365,7 @@ namespace MWWorld if (!player.mObject.mEnabled) { - std::cerr << "Warning: Savegame attempted to disable the player." << std::endl; + Log(Debug::Warning) << "Warning: Savegame attempted to disable the player."; player.mObject.mEnabled = true; } @@ -391,7 +392,7 @@ namespace MWWorld } catch (...) { - std::cerr << "Warning: Player cell '" << player.mCellId.mWorldspace << "' no longer exists" << std::endl; + Log(Debug::Warning) << "Warning: Player cell '" << player.mCellId.mWorldspace << "' no longer exists"; // Cell no longer exists. The loader will have to choose a default cell. mCellStore = NULL; } diff --git a/apps/openmw/mwworld/projectilemanager.cpp b/apps/openmw/mwworld/projectilemanager.cpp index a4a22ea4a..f150ff0ba 100644 --- a/apps/openmw/mwworld/projectilemanager.cpp +++ b/apps/openmw/mwworld/projectilemanager.cpp @@ -1,11 +1,12 @@ #include "projectilemanager.hpp" #include -#include #include #include +#include + #include #include @@ -647,7 +648,7 @@ namespace MWWorld } catch(...) { - std::cerr << "Warning: Failed to recreate magic projectile from saved data (id \"" << state.mSpellId << "\" no longer exists?)" << std::endl; + Log(Debug::Warning) << "Warning: Failed to recreate magic projectile from saved data (id \"" << state.mSpellId << "\" no longer exists?)"; return true; } diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 2c2d401d1..ed6dde310 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -1,8 +1,8 @@ #include "scene.hpp" #include -#include +#include #include #include #include @@ -56,7 +56,7 @@ namespace { if (ptr.getRefData().getBaseNode() || physics.getActor(ptr)) { - std::cerr << "Warning: Tried to add " << ptr.getCellRef().getRefId() << " to the scene twice" << std::endl; + Log(Debug::Warning) << "Warning: Tried to add " << ptr.getCellRef().getRefId() << " to the scene twice"; return; } @@ -160,7 +160,7 @@ namespace catch (const std::exception& e) { std::string error ("failed to render '" + ptr.getCellRef().getRefId() + "': "); - std::cerr << error + e.what() << std::endl; + Log(Debug::Error) << error + e.what(); } } @@ -232,7 +232,7 @@ namespace MWWorld void Scene::unloadCell (CellStoreCollection::iterator iter) { - std::cout << "Unloading cell\n"; + Log(Debug::Info) << "Unloading cell " << (*iter)->getCell()->getDescription(); ListAndResetObjectsVisitor visitor; (*iter)->forEach(visitor); @@ -270,7 +270,7 @@ namespace MWWorld if(result.second) { - std::cout << "Loading cell " << cell->getCell()->getDescription() << std::endl; + Log(Debug::Info) << "Loading cell " << cell->getCell()->getDescription(); float verts = ESM::Land::LAND_SIZE; float worldsize = ESM::Land::REAL_SIZE; @@ -546,7 +546,7 @@ namespace MWWorld return; } - std::cout << "Changing to interior\n"; + Log(Debug::Info) << "Changing to interior"; // unload CellStoreCollection::iterator active = mActiveCells.begin(); @@ -624,7 +624,7 @@ namespace MWWorld } catch (std::exception& e) { - std::cerr << "failed to render '" << ptr.getCellRef().getRefId() << "': " << e.what() << std::endl; + Log(Debug::Error) << "failed to render '" << ptr.getCellRef().getRefId() << "': " << e.what(); } } diff --git a/apps/openmw/mwworld/store.cpp b/apps/openmw/mwworld/store.cpp index 6f0a1b49f..2ac78bb85 100644 --- a/apps/openmw/mwworld/store.cpp +++ b/apps/openmw/mwworld/store.cpp @@ -1,5 +1,7 @@ #include "store.hpp" +#include + #include #include @@ -8,7 +10,6 @@ #include #include -#include namespace { @@ -692,7 +693,7 @@ namespace MWWorld if (it_lease != wipecell->mLeasedRefs.end()) wipecell->mLeasedRefs.erase(it_lease); else - std::cerr << "Error: can't find " << it->mRefNum.mIndex << " " << it->mRefNum.mContentFile << " in leasedRefs " << std::endl; + Log(Debug::Error) << "Error: can't find " << it->mRefNum.mIndex << " " << it->mRefNum.mContentFile << " in leasedRefs"; } *itold = *it; } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index f4c2a75f3..3e4f3ccd0 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -3,6 +3,8 @@ #include #include +#include + #include #include #include @@ -1794,7 +1796,7 @@ namespace MWWorld } catch (std::exception& e) { - std::cerr << "Error updating window manager: " << e.what() << std::endl; + Log(Debug::Error) << "Error updating window manager: " << e.what(); } } @@ -3075,7 +3077,7 @@ namespace MWWorld if ( closestMarker.isEmpty() ) { - std::cerr << "Failed to teleport: no closest marker found" << std::endl; + Log(Debug::Warning) << "Failed to teleport: no closest marker found"; return; } @@ -3250,19 +3252,19 @@ namespace MWWorld MWWorld::ConstPtr prisonMarker = getClosestMarker( ptr, "prisonmarker" ); if ( prisonMarker.isEmpty() ) { - std::cerr << "Failed to confiscate items: no closest prison marker found." << std::endl; + Log(Debug::Warning) << "Failed to confiscate items: no closest prison marker found."; return; } std::string prisonName = prisonMarker.getCellRef().getDestCell(); if ( prisonName.empty() ) { - std::cerr << "Failed to confiscate items: prison marker not linked to prison interior" << std::endl; + Log(Debug::Warning) << "Failed to confiscate items: prison marker not linked to prison interior"; return; } MWWorld::CellStore *prison = getInterior( prisonName ); if ( !prison ) { - std::cerr << "Failed to confiscate items: failed to load cell " << prisonName << std::endl; + Log(Debug::Warning) << "Failed to confiscate items: failed to load cell " << prisonName; return; } @@ -3272,7 +3274,7 @@ namespace MWWorld MWBase::Environment::get().getMechanicsManager()->confiscateStolenItems(ptr, closestChest); } else - std::cerr << "Failed to confiscate items: no stolen_goods container found" << std::endl; + Log(Debug::Warning) << "Failed to confiscate items: no stolen_goods container found"; } void World::goToJail() From 4cb9b52b8186fe784c355b7273bc0c85c31b75d7 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Tue, 14 Aug 2018 23:15:30 +0200 Subject: [PATCH 50/53] fix rebase issue --- files/ui/advancedpage.ui | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/files/ui/advancedpage.ui b/files/ui/advancedpage.ui index b308f21ac..59e46a56c 100644 --- a/files/ui/advancedpage.ui +++ b/files/ui/advancedpage.ui @@ -22,11 +22,7 @@ 0 0 641 -<<<<<<< HEAD 998 -======= - 968 ->>>>>>> Fixed merge conflicts @@ -93,7 +89,6 @@ Enchanted weapons are magical -<<<<<<< HEAD @@ -104,12 +99,19 @@ Barter disposition change is permanent - -======= ->>>>>>> Fixed merge conflicts + + + + <html><head/><body><p>Uses the MCP formula (damage * (strength / 40)) to factor the Strength attribute into hand-to-hand combat.</p><p>The default value is false.</p></body></html> + + + Factor strength into hand-to-hand combat + + + From 37f9d4518295f5e998f817acfced0c35654f402f Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Wed, 15 Aug 2018 15:56:31 +0300 Subject: [PATCH 51/53] Remove an outdated checkbox from the advanced page --- apps/launcher/advancedpage.cpp | 4 ---- files/ui/advancedpage.ui | 26 -------------------------- 2 files changed, 30 deletions(-) diff --git a/apps/launcher/advancedpage.cpp b/apps/launcher/advancedpage.cpp index b254e0b01..102463085 100644 --- a/apps/launcher/advancedpage.cpp +++ b/apps/launcher/advancedpage.cpp @@ -76,12 +76,10 @@ bool Launcher::AdvancedPage::loadSettings() loadSettingBool(chargeForEveryFollowerCheckBox, "charge for every follower travelling", "Game"); loadSettingBool(enchantedWeaponsMagicalCheckBox, "enchanted weapons are magical", "Game"); loadSettingBool(permanentBarterDispositionChangeCheckBox, "barter disposition change is permanent", "Game"); - loadSettingBool(strengthInfluencesHandToHand, "strength influences hand to hand", "Game"); int unarmedFactorsStrengthIndex = mEngineSettings.getInt("strength influences hand to hand", "Game"); if (unarmedFactorsStrengthIndex >= 0 && unarmedFactorsStrengthIndex <= 2) unarmedFactorsStrengthComboBox->setCurrentIndex(unarmedFactorsStrengthIndex); - // Input Settings loadSettingBool(allowThirdPersonZoomCheckBox, "allow third person zoom", "Input"); loadSettingBool(grabCursorCheckBox, "grab cursor", "Input"); @@ -136,12 +134,10 @@ void Launcher::AdvancedPage::saveSettings() saveSettingBool(chargeForEveryFollowerCheckBox, "charge for every follower travelling", "Game"); saveSettingBool(enchantedWeaponsMagicalCheckBox, "enchanted weapons are magical", "Game"); saveSettingBool(permanentBarterDispositionChangeCheckBox, "barter disposition change is permanent", "Game"); - saveSettingBool(strengthInfluencesHandToHand, "strength influences hand to hand", "Game"); int unarmedFactorsStrengthIndex = unarmedFactorsStrengthComboBox->currentIndex(); if (unarmedFactorsStrengthIndex != mEngineSettings.getInt("strength influences hand to hand", "Game")) mEngineSettings.setInt("strength influences hand to hand", "Game", unarmedFactorsStrengthIndex); - // Input Settings saveSettingBool(allowThirdPersonZoomCheckBox, "allow third person zoom", "Input"); saveSettingBool(grabCursorCheckBox, "grab cursor", "Input"); diff --git a/files/ui/advancedpage.ui b/files/ui/advancedpage.ui index 59e46a56c..8395d028f 100644 --- a/files/ui/advancedpage.ui +++ b/files/ui/advancedpage.ui @@ -2,14 +2,6 @@ AdvancedPage - - - 0 - 0 - 671 - 373 - - @@ -17,14 +9,6 @@ true - - - 0 - 0 - 641 - 998 - - @@ -102,16 +86,6 @@ - - - - <html><head/><body><p>Uses the MCP formula (damage * (strength / 40)) to factor the Strength attribute into hand-to-hand combat.</p><p>The default value is false.</p></body></html> - - - Factor strength into hand-to-hand combat - - - From 372697489bf3af4c636c59c6f6654fb6424c033e Mon Sep 17 00:00:00 2001 From: Capostrophic <21265616+Capostrophic@users.noreply.github.com> Date: Fri, 17 Aug 2018 02:41:06 +0300 Subject: [PATCH 52/53] Fix Equip command infinite loop (bug #3072) --- CHANGELOG.md | 1 + apps/openmw/mwgui/inventorywindow.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c8eef0d45..7fb19f276 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ Bug #2872: Tab completion in console doesn't work with explicit reference Bug #2971: Compiler did not reject lines with naked expressions beginning with x.y Bug #3049: Drain and Fortify effects are not properly applied on health, magicka and fatigue + Bug #3072: Fatal error on AddItem that has a script containing Equip Bug #3249: Fixed revert function not updating views properly Bug #3374: Touch spells not hitting kwama foragers Bug #3486: [Mod] NPC Commands does not work diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index ce6d0d522..0b35bd9de 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -517,7 +517,7 @@ namespace MWGui // Give the script a chance to run once before we do anything else // this is important when setting pcskipequip as a reaction to onpcequip being set (bk_treasuryreport does this) - if (!script.empty() && MWBase::Environment::get().getWorld()->getScriptsEnabled()) + if (!force && !script.empty() && MWBase::Environment::get().getWorld()->getScriptsEnabled()) { MWScript::InterpreterContext interpreterContext (&ptr.getRefData().getLocals(), ptr); MWBase::Environment::get().getScriptManager()->run (script, interpreterContext); From ce78a34010f129720c44d04263b36088b8d5a7f1 Mon Sep 17 00:00:00 2001 From: Capostrophic <21265616+Capostrophic@users.noreply.github.com> Date: Fri, 17 Aug 2018 03:32:33 +0300 Subject: [PATCH 53/53] Use container ID in Equip command warning --- apps/openmw/mwscript/containerextensions.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwscript/containerextensions.cpp b/apps/openmw/mwscript/containerextensions.cpp index dd3e54a88..cf12d12c3 100644 --- a/apps/openmw/mwscript/containerextensions.cpp +++ b/apps/openmw/mwscript/containerextensions.cpp @@ -192,8 +192,9 @@ namespace MWScript if (it == invStore.end()) { it = ptr.getClass().getContainerStore (ptr).add (item, 1, ptr); - Log(Debug::Warning) << "Implicitly adding one " << item << " to container " - "to fulfil requirements of Equip instruction"; + Log(Debug::Warning) << "Implicitly adding one " << item << + " to the inventory store of " << ptr.getCellRef().getRefId() << + " to fulfill the requirements of Equip instruction"; } if (ptr == MWMechanics::getPlayer())