From adbaeb7ccada43631f580c504ba01a0f2f6a874a Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Mon, 18 Jun 2018 13:43:39 +0400 Subject: [PATCH 001/163] Improve GUI scaling (bug #3288) --- CHANGELOG.md | 1 + apps/openmw/mwbase/windowmanager.hpp | 1 + apps/openmw/mwgui/bookpage.cpp | 45 +++++++------- apps/openmw/mwgui/bookpage.hpp | 46 ++++++++++++++ apps/openmw/mwgui/console.cpp | 6 -- apps/openmw/mwgui/console.hpp | 2 - apps/openmw/mwgui/formatting.cpp | 13 +--- apps/openmw/mwgui/formatting.hpp | 4 +- apps/openmw/mwgui/journalbooks.cpp | 28 ++++++--- apps/openmw/mwgui/journalbooks.hpp | 6 +- apps/openmw/mwgui/journalwindow.cpp | 22 ++++++- apps/openmw/mwgui/merchantrepair.cpp | 11 ++-- apps/openmw/mwgui/merchantrepair.hpp | 2 - apps/openmw/mwgui/review.cpp | 23 +++---- apps/openmw/mwgui/review.hpp | 2 - apps/openmw/mwgui/spellbuyingwindow.cpp | 10 ++-- apps/openmw/mwgui/spellbuyingwindow.hpp | 2 - apps/openmw/mwgui/spellview.cpp | 5 +- apps/openmw/mwgui/statswindow.cpp | 18 +++--- apps/openmw/mwgui/statswindow.hpp | 2 - apps/openmw/mwgui/tooltips.cpp | 7 ++- apps/openmw/mwgui/travelwindow.cpp | 10 ++-- apps/openmw/mwgui/travelwindow.hpp | 2 - apps/openmw/mwgui/windowmanagerimp.cpp | 60 ++++++++++++++++++- apps/openmw/mwgui/windowmanagerimp.hpp | 5 ++ apps/openmw/mwrender/localmap.cpp | 5 ++ components/CMakeLists.txt | 2 +- components/widgets/box.cpp | 9 +-- components/widgets/box.hpp | 26 +++++++- components/widgets/fontwrapper.hpp | 45 ++++++++++++++ components/widgets/numericeditbox.hpp | 7 ++- components/widgets/sharedstatebutton.cpp | 1 - components/widgets/sharedstatebutton.hpp | 4 +- components/widgets/widgets.cpp | 3 + components/widgets/widgets.hpp | 3 +- .../source/reference/modding/settings/GUI.rst | 21 +++++++ files/mygui/core.skin | 1 - files/mygui/openmw_confirmation_dialog.layout | 2 +- files/mygui/openmw_enchanting_dialog.layout | 2 +- files/mygui/openmw_font.xml | 2 - files/mygui/openmw_inventory_window.layout | 2 +- files/mygui/openmw_journal.layout | 5 +- files/mygui/openmw_journal.skin.xml | 6 +- files/mygui/openmw_list.skin.xml | 4 -- files/mygui/openmw_windows.skin.xml | 8 +-- files/settings-default.cfg | 6 ++ 46 files changed, 352 insertions(+), 145 deletions(-) create mode 100644 components/widgets/fontwrapper.hpp diff --git a/CHANGELOG.md b/CHANGELOG.md index 44cb90afd..37bf5e660 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ Bug #3059: Unable to hit with marksman weapons when too close to an enemy Bug #3072: Fatal error on AddItem that has a script containing Equip Bug #3249: Fixed revert function not updating views properly + Bug #3288: TrueType fonts are handled incorrectly 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 diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 39eed5537..b40f517f9 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -219,6 +219,7 @@ namespace MWBase virtual const MWWorld::Ptr& getSelectedEnchantItem() const = 0; virtual void setSelectedWeapon(const MWWorld::Ptr& item) = 0; virtual const MWWorld::Ptr& getSelectedWeapon() const = 0; + virtual int getFontHeight() const = 0; virtual void unsetSelectedSpell() = 0; virtual void unsetSelectedWeapon() = 0; diff --git a/apps/openmw/mwgui/bookpage.cpp b/apps/openmw/mwgui/bookpage.cpp index 6f16cf076..8f2e8b5ed 100644 --- a/apps/openmw/mwgui/bookpage.cpp +++ b/apps/openmw/mwgui/bookpage.cpp @@ -1,6 +1,5 @@ #include "bookpage.hpp" -#include "MyGUI_FontManager.h" #include "MyGUI_RenderItem.h" #include "MyGUI_RenderManager.h" #include "MyGUI_TextureUtility.h" @@ -8,6 +7,9 @@ #include +#include "../mwbase/environment.hpp" +#include "../mwbase/windowmanager.hpp" + namespace MWGui { struct TypesetBookImpl; @@ -497,9 +499,9 @@ struct TypesetBookImpl::Typesetter : BookTypesetter while (!stream.eof () && !ucsLineBreak (stream.peek ()) && ucsBreakingSpace (stream.peek ())) { - MyGUI::GlyphInfo* gi = style->mFont->getGlyphInfo (stream.peek ()); - if (gi) - space_width += static_cast(gi->advance + gi->bearingX); + MWGui::GlyphInfo info = GlyphInfo(style->mFont, stream.peek()); + if (info.codePoint >= 0) + space_width += static_cast(info.advance + info.bearingX); stream.consume (); } @@ -507,9 +509,9 @@ struct TypesetBookImpl::Typesetter : BookTypesetter while (!stream.eof () && !ucsLineBreak (stream.peek ()) && !ucsBreakingSpace (stream.peek ())) { - MyGUI::GlyphInfo* gi = style->mFont->getGlyphInfo (stream.peek ()); - if (gi) - word_width += static_cast(gi->advance + gi->bearingX); + MWGui::GlyphInfo info = GlyphInfo(style->mFont, stream.peek()); + if (info.codePoint >= 0) + word_width += static_cast(info.advance + info.bearingX); stream.consume (); } @@ -530,6 +532,7 @@ struct TypesetBookImpl::Typesetter : BookTypesetter if (mPartialWhitespace.empty() && mPartialWord.empty()) return; + int fontHeight = MWBase::Environment::get().getWindowManager()->getFontHeight(); int space_width = 0; int word_width = 0; @@ -549,9 +552,8 @@ struct TypesetBookImpl::Typesetter : BookTypesetter for (PartialTextConstIterator i = mPartialWhitespace.begin (); i != mPartialWhitespace.end (); ++i) { int top = mLine ? mLine->mRect.top : mBook->mRect.bottom; - int line_height = i->mStyle->mFont->getDefaultHeight (); - append_run ( i->mStyle, i->mBegin, i->mEnd, 0, left + i->mWidth, top + line_height); + append_run ( i->mStyle, i->mBegin, i->mEnd, 0, left + i->mWidth, top + fontHeight); left = mLine->mRect.right; } @@ -560,9 +562,8 @@ struct TypesetBookImpl::Typesetter : BookTypesetter for (PartialTextConstIterator i = mPartialWord.begin (); i != mPartialWord.end (); ++i) { int top = mLine ? mLine->mRect.top : mBook->mRect.bottom; - int line_height = i->mStyle->mFont->getDefaultHeight (); - append_run (i->mStyle, i->mBegin, i->mEnd, i->mEnd - i->mBegin, left + i->mWidth, top + line_height); + append_run (i->mStyle, i->mBegin, i->mEnd, i->mEnd - i->mBegin, left + i->mWidth, top + fontHeight); left = mLine->mRect.right; } @@ -756,32 +757,32 @@ namespace void emitGlyph (wchar_t ch) { - MyGUI::GlyphInfo* gi = mFont->getGlyphInfo (ch); + MWGui::GlyphInfo info = GlyphInfo(mFont, ch); - if (!gi) + if (info.codePoint < 0) return; MyGUI::FloatRect vr; - vr.left = mCursor.left + gi->bearingX; - vr.top = mCursor.top + gi->bearingY; - vr.right = vr.left + gi->width; - vr.bottom = vr.top + gi->height; + vr.left = mCursor.left + info.bearingX; + vr.top = mCursor.top + info.bearingY; + vr.right = vr.left + info.width; + vr.bottom = vr.top + info.height; - MyGUI::FloatRect tr = gi->uvRect; + MyGUI::FloatRect tr = info.uvRect; if (mRenderXform.clip (vr, tr)) quad (vr, tr); - mCursor.left += gi->bearingX + gi->advance; + mCursor.left += static_cast(info.bearingX + info.advance); } void emitSpace (wchar_t ch) { - MyGUI::GlyphInfo* gi = mFont->getGlyphInfo (ch); + MWGui::GlyphInfo info = GlyphInfo(mFont, ch); - if (gi) - mCursor.left += gi->bearingX + gi->advance; + if (info.codePoint >= 0) + mCursor.left += static_cast(info.bearingX + info.advance); } private: diff --git a/apps/openmw/mwgui/bookpage.hpp b/apps/openmw/mwgui/bookpage.hpp index 66d1834c7..b8174bd3d 100644 --- a/apps/openmw/mwgui/bookpage.hpp +++ b/apps/openmw/mwgui/bookpage.hpp @@ -3,10 +3,17 @@ #include "MyGUI_Colour.h" #include "MyGUI_Widget.h" +#include "MyGUI_FontManager.h" #include #include +#include +#include + +#include "../mwbase/environment.hpp" +#include "../mwbase/windowmanager.hpp" + namespace MWGui { /// A formatted and paginated document to be used with @@ -28,6 +35,45 @@ namespace MWGui virtual std::pair getSize () const = 0; }; + struct GlyphInfo + { + char codePoint; + float width; + float height; + float advance; + float bearingX; + float bearingY; + MyGUI::FloatRect uvRect; + + GlyphInfo(MyGUI::IFont* font, MyGUI::Char ch) + { + static const int fontHeight = MWBase::Environment::get().getWindowManager()->getFontHeight(); + + MyGUI::GlyphInfo* gi = font->getGlyphInfo(ch); + if (gi) + { + const float scale = font->getDefaultHeight() / (float) fontHeight; + + codePoint = gi->codePoint; + bearingX = (int) gi->bearingX / scale; + bearingY = (int) gi->bearingY / scale; + width = (int) gi->width / scale; + height = (int) gi->height / scale; + advance = (int) gi->advance / scale; + uvRect = gi->uvRect; + } + else + { + codePoint = -1; + bearingX = 0; + bearingY = 0; + width = 0; + height = 0; + advance = 0; + } + } + }; + /// A factory class for creating a typeset book instance. struct BookTypesetter { diff --git a/apps/openmw/mwgui/console.cpp b/apps/openmw/mwgui/console.cpp index b367c6f49..dc22e4193 100644 --- a/apps/openmw/mwgui/console.cpp +++ b/apps/openmw/mwgui/console.cpp @@ -152,12 +152,6 @@ namespace MWGui MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCommandLine); } - void Console::setFont(const std::string &fntName) - { - mHistory->setFontName(fntName); - mCommandLine->setFontName(fntName); - } - void Console::print(const std::string &msg, const std::string& color) { mHistory->addText(color + MyGUI::TextIterator::toTagsString(msg)); diff --git a/apps/openmw/mwgui/console.hpp b/apps/openmw/mwgui/console.hpp index bbff34c8d..883bc8967 100644 --- a/apps/openmw/mwgui/console.hpp +++ b/apps/openmw/mwgui/console.hpp @@ -41,8 +41,6 @@ namespace MWGui virtual void onOpen(); - void setFont(const std::string &fntName); - void onResChange(int width, int height); // Print a message to the console, in specified color. diff --git a/apps/openmw/mwgui/formatting.cpp b/apps/openmw/mwgui/formatting.cpp index 663bd7338..157aeba26 100644 --- a/apps/openmw/mwgui/formatting.cpp +++ b/apps/openmw/mwgui/formatting.cpp @@ -415,7 +415,7 @@ namespace MWGui : GraphicElement(parent, pag, blockStyle), mTextStyle(textStyle) { - MyGUI::EditBox* box = parent->createWidget("NormalText", + Gui::EditBox* box = parent->createWidget("NormalText", MyGUI::IntCoord(0, pag.getCurrentTop(), pag.getPageWidth(), 0), MyGUI::Align::Left | MyGUI::Align::Top, parent->getName() + MyGUI::utility::toString(parent->getChildCount())); box->setEditStatic(true); @@ -432,15 +432,6 @@ namespace MWGui mEditBox = box; } - int TextElement::currentFontHeight() const - { - std::string fontName(mTextStyle.mFont == "Default" ? MyGUI::FontManager::getInstance().getDefaultFont() : mTextStyle.mFont); - MyGUI::IFont* font = MyGUI::FontManager::getInstance().getByName(fontName); - if (!font) - return 0; - return font->getDefaultHeight(); - } - int TextElement::getHeight() { return mEditBox->getTextSize().height; @@ -449,7 +440,7 @@ namespace MWGui int TextElement::pageSplit() { // split lines - const int lineHeight = currentFontHeight(); + const int lineHeight = MWBase::Environment::get().getWindowManager()->getFontHeight(); unsigned int lastLine = (mPaginator.getStartTop() + mPaginator.getPageHeight() - mPaginator.getCurrentTop()); if (lineHeight > 0) lastLine /= lineHeight; diff --git a/apps/openmw/mwgui/formatting.hpp b/apps/openmw/mwgui/formatting.hpp index d525baace..7ccb0824e 100644 --- a/apps/openmw/mwgui/formatting.hpp +++ b/apps/openmw/mwgui/formatting.hpp @@ -4,6 +4,8 @@ #include #include +#include + namespace MWGui { namespace Formatting @@ -152,7 +154,7 @@ namespace MWGui private: int currentFontHeight() const; TextStyle mTextStyle; - MyGUI::EditBox * mEditBox; + Gui::EditBox * mEditBox; }; class ImageElement : public GraphicElement diff --git a/apps/openmw/mwgui/journalbooks.cpp b/apps/openmw/mwgui/journalbooks.cpp index 1075239fa..1956ee1c2 100644 --- a/apps/openmw/mwgui/journalbooks.cpp +++ b/apps/openmw/mwgui/journalbooks.cpp @@ -218,21 +218,24 @@ book JournalBooks::createQuestBook (const std::string& questName) return typesetter->complete (); } -book JournalBooks::createTopicIndexBook () +book JournalBooks::createTopicIndexBook (int& columnsCount) { bool isRussian = (mEncoding == ToUTF8::WINDOWS_1251); - BookTypesetter::Ptr typesetter = isRussian ? createCyrillicJournalIndex() : createLatinJournalIndex(); + BookTypesetter::Ptr typesetter = isRussian ? createCyrillicJournalIndex(columnsCount) : createLatinJournalIndex(columnsCount); return typesetter->complete (); } -BookTypesetter::Ptr JournalBooks::createLatinJournalIndex () +BookTypesetter::Ptr JournalBooks::createLatinJournalIndex (int& columnsCount) { - BookTypesetter::Ptr typesetter = BookTypesetter::create (92, 250); + BookTypesetter::Ptr typesetter = BookTypesetter::create (92, 260); typesetter->setSectionAlignment (BookTypesetter::AlignCenter); + // Latin journal index always has two columns for now. + columnsCount = 2; + char ch = 'A'; BookTypesetter::Style* body = typesetter->createStyle ("", MyGUI::Colour::Black); @@ -258,14 +261,25 @@ BookTypesetter::Ptr JournalBooks::createLatinJournalIndex () return typesetter; } -BookTypesetter::Ptr JournalBooks::createCyrillicJournalIndex () +BookTypesetter::Ptr JournalBooks::createCyrillicJournalIndex (int& columnsCount) { - BookTypesetter::Ptr typesetter = BookTypesetter::create (92, 250); + BookTypesetter::Ptr typesetter = BookTypesetter::create (92, 260); typesetter->setSectionAlignment (BookTypesetter::AlignCenter); BookTypesetter::Style* body = typesetter->createStyle ("", MyGUI::Colour::Black); + int fontHeight = MWBase::Environment::get().getWindowManager()->getFontHeight(); + + // for small font size split alphabet to two columns (2x15 characers), for big font size split it to three colums (3x10 characters). + int sectionBreak = 10; + columnsCount = 3; + if (fontHeight < 18) + { + sectionBreak = 15; + columnsCount = 2; + } + unsigned char ch[2] = {0xd0, 0x90}; // CYRILLIC CAPITAL A is a 0xd090 in UTF-8 for (int i = 0; i < 32; ++i) @@ -287,7 +301,7 @@ BookTypesetter::Ptr JournalBooks::createCyrillicJournalIndex () if (i == 26 || i == 28) continue; - if (i == 15) + if (i % sectionBreak == 0) typesetter->sectionBreak (); typesetter->write (style, to_utf8_span (buffer)); diff --git a/apps/openmw/mwgui/journalbooks.hpp b/apps/openmw/mwgui/journalbooks.hpp index aa36eecdf..cc6f42cc8 100644 --- a/apps/openmw/mwgui/journalbooks.hpp +++ b/apps/openmw/mwgui/journalbooks.hpp @@ -22,14 +22,14 @@ namespace MWGui Book createTopicBook (uintptr_t topicId); Book createTopicBook (const std::string& topicId); Book createQuestBook (const std::string& questName); - Book createTopicIndexBook (); + Book createTopicIndexBook (int& columnsCount); ToUTF8::FromType mEncoding; private: BookTypesetter::Ptr createTypesetter (); - BookTypesetter::Ptr createLatinJournalIndex (); - BookTypesetter::Ptr createCyrillicJournalIndex (); + BookTypesetter::Ptr createLatinJournalIndex (int& columnsCount); + BookTypesetter::Ptr createCyrillicJournalIndex (int& columnsCount); }; } diff --git a/apps/openmw/mwgui/journalwindow.cpp b/apps/openmw/mwgui/journalwindow.cpp index 0776706d8..cef20159d 100644 --- a/apps/openmw/mwgui/journalwindow.cpp +++ b/apps/openmw/mwgui/journalwindow.cpp @@ -43,6 +43,7 @@ namespace static char const LeftBookPage [] = "LeftBookPage"; static char const RightBookPage [] = "RightBookPage"; static char const LeftTopicIndex [] = "LeftTopicIndex"; + static char const CenterTopicIndex [] = "CenterTopicIndex"; static char const RightTopicIndex [] = "RightTopicIndex"; struct JournalWindowImpl : MWGui::JournalBooks, MWGui::JournalWindow @@ -148,6 +149,7 @@ namespace callback = std::bind(&JournalWindowImpl::notifyIndexLinkClicked, this, std::placeholders::_1); getPage (LeftTopicIndex)->adviseLinkClicked (callback); + getPage (CenterTopicIndex)->adviseLinkClicked (callback); getPage (RightTopicIndex)->adviseLinkClicked (callback); } @@ -312,6 +314,7 @@ namespace setVisible (TopicsList, false); setVisible (QuestsList, mQuestMode); setVisible (LeftTopicIndex, !mQuestMode); + setVisible (CenterTopicIndex, !mQuestMode); setVisible (RightTopicIndex, !mQuestMode); setVisible (ShowAllBTN, mQuestMode && !mAllQuests); setVisible (ShowActiveBTN, mQuestMode && mAllQuests); @@ -462,11 +465,21 @@ namespace { setOptionsMode (); + int pagesCount; if (!mTopicIndexBook) - mTopicIndexBook = createTopicIndexBook (); + mTopicIndexBook = createTopicIndexBook (pagesCount); - getPage (LeftTopicIndex)->showPage (mTopicIndexBook, 0); - getPage (RightTopicIndex)->showPage (mTopicIndexBook, 1); + if (pagesCount == 3) + { + getPage (LeftTopicIndex)->showPage (mTopicIndexBook, 0); + getPage (CenterTopicIndex)->showPage (mTopicIndexBook, 1); + getPage (RightTopicIndex)->showPage (mTopicIndexBook, 2); + } + else + { + getPage (LeftTopicIndex)->showPage (mTopicIndexBook, 0); + getPage (RightTopicIndex)->showPage (mTopicIndexBook, 1); + } } void notifyJournal(MyGUI::Widget* _sender) @@ -480,6 +493,7 @@ namespace void notifyIndexLinkClicked (MWGui::TypesetBook::InteractiveId index) { setVisible (LeftTopicIndex, false); + setVisible (CenterTopicIndex, false); setVisible (RightTopicIndex, false); setVisible (TopicsList, true); @@ -502,6 +516,7 @@ namespace mQuestMode = false; mTopicsMode = false; setVisible (LeftTopicIndex, true); + setVisible (CenterTopicIndex, true); setVisible (RightTopicIndex, true); setVisible (TopicsList, false); setVisible (QuestsList, false); @@ -540,6 +555,7 @@ namespace mQuestMode = true; setVisible (LeftTopicIndex, false); + setVisible (CenterTopicIndex, true); setVisible (RightTopicIndex, false); setVisible (TopicsList, false); setVisible (QuestsList, true); diff --git a/apps/openmw/mwgui/merchantrepair.cpp b/apps/openmw/mwgui/merchantrepair.cpp index 9974c6d16..b9d4c80f4 100644 --- a/apps/openmw/mwgui/merchantrepair.cpp +++ b/apps/openmw/mwgui/merchantrepair.cpp @@ -20,8 +20,6 @@ namespace MWGui { -const int MerchantRepair::sLineHeight = 18; - MerchantRepair::MerchantRepair() : WindowBase("openmw_merchantrepair.layout") { @@ -39,6 +37,7 @@ void MerchantRepair::setPtr(const MWWorld::Ptr &actor) while (mList->getChildCount()) MyGUI::Gui::getInstance().destroyWidget(mList->getChildAt(0)); + int lineHeight = MWBase::Environment::get().getWindowManager()->getFontHeight() + 2; int currentY = 0; MWWorld::Ptr player = MWMechanics::getPlayer(); @@ -67,28 +66,26 @@ void MerchantRepair::setPtr(const MWWorld::Ptr &actor) int price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mActor, x, true); - std::string name = iter->getClass().getName(*iter) + " - " + MyGUI::utility::toString(price) + MWBase::Environment::get().getWorld()->getStore().get() .find("sgp")->mValue.getString(); - MyGUI::Button* button = mList->createWidget(price <= playerGold ? "SandTextButton" : "SandTextButtonDisabled", // can't use setEnabled since that removes tooltip 0, currentY, 0, - sLineHeight, + lineHeight, MyGUI::Align::Default ); - currentY += sLineHeight; + currentY += lineHeight; button->setUserString("Price", MyGUI::utility::toString(price)); button->setUserData(MWWorld::Ptr(*iter)); button->setCaptionWithReplacing(name); - button->setSize(mList->getWidth(),sLineHeight); + button->setSize(mList->getWidth(), lineHeight); button->eventMouseWheel += MyGUI::newDelegate(this, &MerchantRepair::onMouseWheel); button->setUserString("ToolTipType", "ItemPtr"); button->eventMouseButtonClick += MyGUI::newDelegate(this, &MerchantRepair::onRepairButtonClick); diff --git a/apps/openmw/mwgui/merchantrepair.hpp b/apps/openmw/mwgui/merchantrepair.hpp index 4e4e7164f..26887ae2c 100644 --- a/apps/openmw/mwgui/merchantrepair.hpp +++ b/apps/openmw/mwgui/merchantrepair.hpp @@ -27,8 +27,6 @@ protected: void onMouseWheel(MyGUI::Widget* _sender, int _rel); void onRepairButtonClick(MyGUI::Widget* sender); void onOkButtonClick(MyGUI::Widget* sender); - - static const int sLineHeight; }; } diff --git a/apps/openmw/mwgui/review.cpp b/apps/openmw/mwgui/review.cpp index 61a7a2ac0..b59b1582c 100644 --- a/apps/openmw/mwgui/review.cpp +++ b/apps/openmw/mwgui/review.cpp @@ -26,9 +26,6 @@ namespace namespace MWGui { - - const int ReviewDialog::sLineHeight = 18; - ReviewDialog::ReviewDialog() : WindowModal("openmw_chargen_review.layout"), mUpdateSkillArea(false) @@ -261,8 +258,9 @@ namespace MWGui groupWidget->setCaption(label); mSkillWidgets.push_back(groupWidget); - coord1.top += sLineHeight; - coord2.top += sLineHeight; + int lineHeight = MWBase::Environment::get().getWindowManager()->getFontHeight() + 2; + coord1.top += lineHeight; + coord2.top += lineHeight; } MyGUI::TextBox* ReviewDialog::addValueItem(const std::string& text, const std::string &value, const std::string& state, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) @@ -282,8 +280,9 @@ namespace MWGui mSkillWidgets.push_back(skillNameWidget); mSkillWidgets.push_back(skillValueWidget); - coord1.top += sLineHeight; - coord2.top += sLineHeight; + int lineHeight = MWBase::Environment::get().getWindowManager()->getFontHeight() + 2; + coord1.top += lineHeight; + coord2.top += lineHeight; return skillValueWidget; } @@ -298,8 +297,9 @@ namespace MWGui mSkillWidgets.push_back(skillNameWidget); - coord1.top += sLineHeight; - coord2.top += sLineHeight; + int lineHeight = MWBase::Environment::get().getWindowManager()->getFontHeight() + 2; + coord1.top += lineHeight; + coord2.top += lineHeight; } void ReviewDialog::addItem(const ESM::Spell* spell, MyGUI::IntCoord& coord1, MyGUI::IntCoord& coord2) @@ -312,8 +312,9 @@ namespace MWGui mSkillWidgets.push_back(widget); - coord1.top += sLineHeight; - coord2.top += sLineHeight; + int lineHeight = MWBase::Environment::get().getWindowManager()->getFontHeight() + 2; + coord1.top += lineHeight; + coord2.top += lineHeight; } void ReviewDialog::addSkills(const SkillList &skills, const std::string &titleId, const std::string &titleDefault, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) diff --git a/apps/openmw/mwgui/review.hpp b/apps/openmw/mwgui/review.hpp index 8e9ec0ec7..f46ad280d 100644 --- a/apps/openmw/mwgui/review.hpp +++ b/apps/openmw/mwgui/review.hpp @@ -87,8 +87,6 @@ namespace MWGui void addItem(const ESM::Spell* spell, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2); void updateSkillArea(); - static const int sLineHeight; - MyGUI::TextBox *mNameWidget, *mRaceWidget, *mClassWidget, *mBirthSignWidget; MyGUI::ScrollView* mSkillView; diff --git a/apps/openmw/mwgui/spellbuyingwindow.cpp b/apps/openmw/mwgui/spellbuyingwindow.cpp index 9876013f1..bc5d161d8 100644 --- a/apps/openmw/mwgui/spellbuyingwindow.cpp +++ b/apps/openmw/mwgui/spellbuyingwindow.cpp @@ -18,8 +18,6 @@ namespace MWGui { - const int SpellBuyingWindow::sLineHeight = 18; - SpellBuyingWindow::SpellBuyingWindow() : WindowBase("openmw_spell_buying_window.layout") , mCurrentY(0) @@ -52,21 +50,23 @@ namespace MWGui // TODO: refactor to use MyGUI::ListBox + int lineHeight = MWBase::Environment::get().getWindowManager()->getFontHeight() + 2; + MyGUI::Button* toAdd = mSpellsView->createWidget( price <= playerGold ? "SandTextButton" : "SandTextButtonDisabled", // can't use setEnabled since that removes tooltip 0, mCurrentY, 200, - sLineHeight, + lineHeight, MyGUI::Align::Default ); - mCurrentY += sLineHeight; + mCurrentY += lineHeight; toAdd->setUserData(price); toAdd->setCaptionWithReplacing(spell.mName+" - "+MyGUI::utility::toString(price)+"#{sgp}"); - toAdd->setSize(mSpellsView->getWidth(),sLineHeight); + toAdd->setSize(mSpellsView->getWidth(), lineHeight); toAdd->eventMouseWheel += MyGUI::newDelegate(this, &SpellBuyingWindow::onMouseWheel); toAdd->setUserString("ToolTipType", "Spell"); toAdd->setUserString("Spell", spell.mId); diff --git a/apps/openmw/mwgui/spellbuyingwindow.hpp b/apps/openmw/mwgui/spellbuyingwindow.hpp index 3414c1b94..30727a545 100644 --- a/apps/openmw/mwgui/spellbuyingwindow.hpp +++ b/apps/openmw/mwgui/spellbuyingwindow.hpp @@ -48,8 +48,6 @@ namespace MWGui void clearSpells(); int mCurrentY; - static const int sLineHeight; - void updateLabels(); virtual void onReferenceUnavailable(); diff --git a/apps/openmw/mwgui/spellview.cpp b/apps/openmw/mwgui/spellview.cpp index 758e6b306..702c2f840 100644 --- a/apps/openmw/mwgui/spellview.cpp +++ b/apps/openmw/mwgui/spellview.cpp @@ -6,6 +6,7 @@ #include #include +#include #include "tooltips.hpp" @@ -240,7 +241,7 @@ namespace MWGui mLines.push_back(LineInfo(separator, (MyGUI::Widget*)NULL, NoSpellIndex)); } - MyGUI::TextBox* groupWidget = mScrollView->createWidget("SandBrightText", + MyGUI::TextBox* groupWidget = mScrollView->createWidget("SandBrightText", MyGUI::IntCoord(0, 0, mScrollView->getWidth(), 24), MyGUI::Align::Left | MyGUI::Align::Top); groupWidget->setCaptionWithReplacing(label); @@ -249,7 +250,7 @@ namespace MWGui if (label2 != "") { - MyGUI::TextBox* groupWidget2 = mScrollView->createWidget("SandBrightText", + MyGUI::TextBox* groupWidget2 = mScrollView->createWidget("SandBrightText", MyGUI::IntCoord(0, 0, mScrollView->getWidth(), 24), MyGUI::Align::Left | MyGUI::Align::Top); groupWidget2->setCaptionWithReplacing(label2); diff --git a/apps/openmw/mwgui/statswindow.cpp b/apps/openmw/mwgui/statswindow.cpp index db73351bf..dfa029467 100644 --- a/apps/openmw/mwgui/statswindow.cpp +++ b/apps/openmw/mwgui/statswindow.cpp @@ -23,9 +23,6 @@ namespace MWGui { - - const int StatsWindow::sLineHeight = 18; - StatsWindow::StatsWindow (DragAndDrop* drag) : WindowPinnableBase("openmw_stats_window.layout") , NoDrop(drag, mMainWidget) @@ -376,8 +373,9 @@ namespace MWGui groupWidget->eventMouseWheel += MyGUI::newDelegate(this, &StatsWindow::onMouseWheel); mSkillWidgets.push_back(groupWidget); - coord1.top += sLineHeight; - coord2.top += sLineHeight; + int lineHeight = MWBase::Environment::get().getWindowManager()->getFontHeight() + 2; + coord1.top += lineHeight; + coord2.top += lineHeight; } std::pair StatsWindow::addValueItem(const std::string& text, const std::string &value, const std::string& state, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) @@ -401,8 +399,9 @@ namespace MWGui mSkillWidgets.push_back(skillNameWidget); mSkillWidgets.push_back(skillValueWidget); - coord1.top += sLineHeight; - coord2.top += sLineHeight; + int lineHeight = MWBase::Environment::get().getWindowManager()->getFontHeight() + 2; + coord1.top += lineHeight; + coord2.top += lineHeight; return std::make_pair(skillNameWidget, skillValueWidget); } @@ -421,8 +420,9 @@ namespace MWGui mSkillWidgets.push_back(skillNameWidget); - coord1.top += sLineHeight; - coord2.top += sLineHeight; + int lineHeight = MWBase::Environment::get().getWindowManager()->getFontHeight() + 2; + coord1.top += lineHeight; + coord2.top += lineHeight; return skillNameWidget; } diff --git a/apps/openmw/mwgui/statswindow.hpp b/apps/openmw/mwgui/statswindow.hpp index 487953e00..8dab2f3d9 100644 --- a/apps/openmw/mwgui/statswindow.hpp +++ b/apps/openmw/mwgui/statswindow.hpp @@ -53,8 +53,6 @@ namespace MWGui void onWindowResize(MyGUI::Window* window); void onMouseWheel(MyGUI::Widget* _sender, int _rel); - static const int sLineHeight; - MyGUI::Widget* mLeftPane; MyGUI::Widget* mRightPane; diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index 20814aac5..0bd0d191c 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -8,6 +8,7 @@ #include #include +#include #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" @@ -421,7 +422,7 @@ namespace MWGui std::string realImage = MWBase::Environment::get().getWindowManager()->correctIconPath(image); - MyGUI::EditBox* captionWidget = mDynamicToolTipBox->createWidget("NormalText", MyGUI::IntCoord(0, 0, 300, 300), MyGUI::Align::Left | MyGUI::Align::Top, "ToolTipCaption"); + Gui::EditBox* captionWidget = mDynamicToolTipBox->createWidget("NormalText", MyGUI::IntCoord(0, 0, 300, 300), MyGUI::Align::Left | MyGUI::Align::Top, "ToolTipCaption"); captionWidget->setEditStatic(true); captionWidget->setNeedKeyFocus(false); captionWidget->setCaptionWithReplacing(caption); @@ -429,7 +430,7 @@ namespace MWGui int captionHeight = std::max(caption != "" ? captionSize.height : 0, imageSize); - MyGUI::EditBox* textWidget = mDynamicToolTipBox->createWidget("SandText", MyGUI::IntCoord(0, captionHeight+imageCaptionVPadding, 300, 300-captionHeight-imageCaptionVPadding), MyGUI::Align::Stretch, "ToolTipText"); + Gui::EditBox* textWidget = mDynamicToolTipBox->createWidget("SandText", MyGUI::IntCoord(0, captionHeight+imageCaptionVPadding, 300, 300-captionHeight-imageCaptionVPadding), MyGUI::Align::Stretch, "ToolTipText"); textWidget->setEditStatic(true); textWidget->setEditMultiLine(true); textWidget->setEditWordWrap(info.wordWrap); @@ -447,7 +448,7 @@ namespace MWGui MyGUI::ImageBox* icon = mDynamicToolTipBox->createWidget("MarkerButton", MyGUI::IntCoord(padding.left, totalSize.height+padding.top, 8, 8), MyGUI::Align::Default); icon->setColour(MyGUI::Colour(1.0f, 0.3f, 0.3f)); - MyGUI::EditBox* edit = mDynamicToolTipBox->createWidget("SandText", + Gui::EditBox* edit = mDynamicToolTipBox->createWidget("SandText", MyGUI::IntCoord(padding.left+8+4, totalSize.height+padding.top, 300-padding.left-8-4, 300-totalSize.height), MyGUI::Align::Default); edit->setEditMultiLine(true); diff --git a/apps/openmw/mwgui/travelwindow.cpp b/apps/openmw/mwgui/travelwindow.cpp index 23a5b322f..5f5430524 100644 --- a/apps/openmw/mwgui/travelwindow.cpp +++ b/apps/openmw/mwgui/travelwindow.cpp @@ -22,8 +22,6 @@ namespace MWGui { - const int TravelWindow::sLineHeight = 18; - TravelWindow::TravelWindow() : WindowBase("openmw_travel_window.layout") , mCurrentY(0) @@ -79,9 +77,11 @@ namespace MWGui else price *= std::max(1, static_cast(followers.size())); - MyGUI::Button* toAdd = mDestinationsView->createWidget("SandTextButton", 0, mCurrentY, 200, sLineHeight, MyGUI::Align::Default); + int lineHeight = MWBase::Environment::get().getWindowManager()->getFontHeight() + 2; + + MyGUI::Button* toAdd = mDestinationsView->createWidget("SandTextButton", 0, mCurrentY, 200, lineHeight, MyGUI::Align::Default); toAdd->setEnabled(price <= playerGold); - mCurrentY += sLineHeight; + mCurrentY += lineHeight; if(interior) toAdd->setUserString("interior","y"); else @@ -92,7 +92,7 @@ namespace MWGui toAdd->setUserString("price",oss.str()); toAdd->setCaptionWithReplacing("#{sCell=" + name + "} - " + MyGUI::utility::toString(price)+"#{sgp}"); - toAdd->setSize(mDestinationsView->getWidth(),sLineHeight); + toAdd->setSize(mDestinationsView->getWidth(),lineHeight); toAdd->eventMouseWheel += MyGUI::newDelegate(this, &TravelWindow::onMouseWheel); toAdd->setUserString("Destination", name); toAdd->setUserData(pos); diff --git a/apps/openmw/mwgui/travelwindow.hpp b/apps/openmw/mwgui/travelwindow.hpp index 5ae466047..dcf0b7727 100644 --- a/apps/openmw/mwgui/travelwindow.hpp +++ b/apps/openmw/mwgui/travelwindow.hpp @@ -41,8 +41,6 @@ namespace MWGui void clearDestinations(); int mCurrentY; - static const int sLineHeight; - void updateLabels(); virtual void onReferenceUnavailable(); diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 6917e2e8a..493a14a1c 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -44,7 +44,6 @@ #include -#include #include #include @@ -196,6 +195,7 @@ namespace MWGui , mFallbackMap(fallbackMap) , mShowOwned(0) , mEncoding(encoding) + , mFontHeight(16) , mVersionDescription(versionDescription) { float uiScale = Settings::Manager::getFloat("scaling factor", "GUI"); @@ -233,6 +233,13 @@ namespace MWGui SpellView::registerComponents(); Gui::registerAllWidgets(); + int fontSize = Settings::Manager::getInt("font size", "GUI"); + fontSize = std::min(std::max(12, fontSize), 20); + mFontHeight = fontSize; + + MyGUI::ResourceManager::getInstance().unregisterLoadXmlDelegate("Resource"); + MyGUI::ResourceManager::getInstance().registerLoadXmlDelegate("Resource") = newDelegate(this, &WindowManager::loadFontDelegate); + MyGUI::FactoryManager::getInstance().registerFactory("Controller"); MyGUI::FactoryManager::getInstance().registerFactory("Controller"); @@ -284,6 +291,51 @@ namespace MWGui mShowOwned = Settings::Manager::getInt("show owned", "Game"); } + void WindowManager::loadFontDelegate(MyGUI::xml::ElementPtr _node, const std::string& _file, MyGUI::Version _version) + { + MyGUI::xml::ElementEnumerator root = _node->getElementEnumerator(); + while (root.next("Resource")) + { + std::string type, name; + root->findAttribute("type", type); + root->findAttribute("name", name); + + if (name.empty()) + continue; + + if (Misc::StringUtils::ciEqual(type, "ResourceTrueTypeFont")) + { + // For TrueType fonts we should override Size and Resolution properties + // to allow to configure font size via config file, without need to edit XML file. + // Also we should take UI scaling factor in account + int resolution = Settings::Manager::getInt("ttf resolution", "GUI"); + resolution = std::max(0, resolution); + + float uiScale = Settings::Manager::getFloat("scaling factor", "GUI"); + + if (uiScale > 1.0f) + resolution *= uiScale; + + MyGUI::xml::ElementPtr resolutionNode = root->createChild("Property"); + resolutionNode->addAttribute("key", "Resolution"); + resolutionNode->addAttribute("value", std::to_string(resolution)); + + MyGUI::xml::ElementPtr sizeNode = root->createChild("Property"); + sizeNode->addAttribute("key", "Size"); + sizeNode->addAttribute("value", std::to_string(mFontHeight)); + } + else if (Misc::StringUtils::ciEqual(type, "ResourceSkin")) + { + // We should adjust line height for MyGUI widgets depending on font size + MyGUI::xml::ElementPtr heightNode = root->createChild("Property"); + heightNode->addAttribute("key", "HeightLine"); + heightNode->addAttribute("value", std::to_string(mFontHeight+2)); + } + } + + MyGUI::ResourceManager::getInstance().loadFromXmlNode(_node, _file, _version); + } + void WindowManager::initUI() { // Get size info from the Gui object @@ -504,6 +556,11 @@ namespace MWGui updateVisible(); } + int WindowManager::getFontHeight() const + { + return mFontHeight; + } + void WindowManager::setNewGame(bool newgame) { if (newgame) @@ -522,6 +579,7 @@ namespace MWGui { mKeyboardNavigation.reset(); + MyGUI::ResourceManager::getInstance().unregisterLoadXmlDelegate("Resource"); MyGUI::LanguageManager::getInstance().eventRequestTag.clear(); MyGUI::PointerManager::getInstance().eventChangeMousePointer.clear(); MyGUI::InputManager::getInstance().eventChangeKeyFocus.clear(); diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 657548397..f3bf6d00f 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -246,6 +246,7 @@ namespace MWGui virtual const MWWorld::Ptr& getSelectedEnchantItem() const; virtual void setSelectedWeapon(const MWWorld::Ptr& item); virtual const MWWorld::Ptr& getSelectedWeapon() const; + virtual int getFontHeight() const; virtual void unsetSelectedSpell(); virtual void unsetSelectedWeapon(); @@ -401,6 +402,8 @@ namespace MWGui MWWorld::Ptr mSelectedEnchantItem; MWWorld::Ptr mSelectedWeapon; + void loadFontDelegate(MyGUI::xml::ElementPtr _node, const std::string& _file, MyGUI::Version _version); + std::vector mCurrentModals; // Markers placed manually by the player. Must be shared between both map views (the HUD map and the map window). @@ -513,6 +516,8 @@ namespace MWGui ToUTF8::FromType mEncoding; + int mFontHeight; + std::string mVersionDescription; MWGui::TextColours mTextColours; diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index 5e501ecf8..8ed3441de 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -76,6 +76,11 @@ LocalMap::LocalMap(osg::Group* root) , mAngle(0.f) , mInterior(false) { + // Increase map resolution, if use UI scaling + float uiScale = Settings::Manager::getFloat("scaling factor", "GUI"); + if (uiScale > 1.0) + mMapResolution *= uiScale; + SceneUtil::FindByNameVisitor find("Scene Root"); mRoot->accept(find); mSceneRoot = find.mFoundNode; diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 551767f8f..8e798455f 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -130,7 +130,7 @@ add_component_dir (myguiplatform ) add_component_dir (widgets - box imagebutton tags list numericeditbox sharedstatebutton windowcaption widgets + box fontwrapper imagebutton tags list numericeditbox sharedstatebutton windowcaption widgets ) add_component_dir (fontloader diff --git a/components/widgets/box.cpp b/components/widgets/box.cpp index bf18cef5b..d38410a2c 100644 --- a/components/widgets/box.cpp +++ b/components/widgets/box.cpp @@ -48,7 +48,7 @@ namespace Gui } else { - TextBox::setPropertyOverride (_key, _value); + Gui::TextBox::setPropertyOverride (_key, _value); } } @@ -81,11 +81,10 @@ namespace Gui } else { - EditBox::setPropertyOverride (_key, _value); + Gui::EditBox::setPropertyOverride (_key, _value); } } - MyGUI::IntSize AutoSizedButton::getRequestedSize() { MyGUI::IntSize padding(24, 8); @@ -111,16 +110,14 @@ namespace Gui } else { - Button::setPropertyOverride (_key, _value); + Gui::Button::setPropertyOverride (_key, _value); } } - Box::Box() : mSpacing(4) , mPadding(0) , mAutoResize(false) { - } void Box::notifyChildrenSizeChanged () diff --git a/components/widgets/box.hpp b/components/widgets/box.hpp index 66be00719..9bab575c4 100644 --- a/components/widgets/box.hpp +++ b/components/widgets/box.hpp @@ -4,10 +4,27 @@ #include #include #include +#include #include +#include "fontwrapper.hpp" + namespace Gui { + class Button : public FontWrapper + { + MYGUI_RTTI_DERIVED( Button ) + }; + + class TextBox : public FontWrapper + { + MYGUI_RTTI_DERIVED( TextBox ) + }; + + class EditBox : public FontWrapper + { + MYGUI_RTTI_DERIVED( EditBox ) + }; class AutoSizedWidget { @@ -22,7 +39,7 @@ namespace Gui MyGUI::Align mExpandDirection; }; - class AutoSizedTextBox : public AutoSizedWidget, public MyGUI::TextBox + class AutoSizedTextBox : public AutoSizedWidget, public TextBox { MYGUI_RTTI_DERIVED( AutoSizedTextBox ) @@ -32,9 +49,10 @@ namespace Gui protected: virtual void setPropertyOverride(const std::string& _key, const std::string& _value); + std::string mFontSize; }; - class AutoSizedEditBox : public AutoSizedWidget, public MyGUI::EditBox + class AutoSizedEditBox : public AutoSizedWidget, public EditBox { MYGUI_RTTI_DERIVED( AutoSizedEditBox ) @@ -47,9 +65,10 @@ namespace Gui protected: virtual void setPropertyOverride(const std::string& _key, const std::string& _value); + std::string mFontSize; }; - class AutoSizedButton : public AutoSizedWidget, public MyGUI::Button + class AutoSizedButton : public AutoSizedWidget, public Button { MYGUI_RTTI_DERIVED( AutoSizedButton ) @@ -59,6 +78,7 @@ namespace Gui protected: virtual void setPropertyOverride(const std::string& _key, const std::string& _value); + std::string mFontSize; }; /** diff --git a/components/widgets/fontwrapper.hpp b/components/widgets/fontwrapper.hpp new file mode 100644 index 000000000..2424b22a7 --- /dev/null +++ b/components/widgets/fontwrapper.hpp @@ -0,0 +1,45 @@ +#ifndef OPENMW_WIDGETS_WRAPPER_H +#define OPENMW_WIDGETS_WRAPPER_H + +#include "widgets.hpp" + +#include + +namespace Gui +{ + template + class FontWrapper : public T + { + public: + virtual void setFontName(const std::string& name) + { + T::setFontName(name); + T::setPropertyOverride ("FontHeight", mFontSize); + } + + protected: + FontWrapper() + { + // Note: we can not use the WindowManager here, so there is a code duplication a bit. + int fontSize = Settings::Manager::getInt("font size", "GUI"); + fontSize = std::min(std::max(12, fontSize), 20); + mFontSize = std::to_string(fontSize); + } + + virtual void setPropertyOverride(const std::string& _key, const std::string& _value) + { + T::setPropertyOverride (_key, _value); + + // There is a bug in MyGUI: when it initializes the FontName property, it reset the font height. + // We should restore it. + if (_key == "FontName") + { + T::setPropertyOverride ("FontHeight", mFontSize); + } + } + + std::string mFontSize; + }; +} + +#endif diff --git a/components/widgets/numericeditbox.hpp b/components/widgets/numericeditbox.hpp index ca7674f22..3edae2fc7 100644 --- a/components/widgets/numericeditbox.hpp +++ b/components/widgets/numericeditbox.hpp @@ -3,13 +3,15 @@ #include +#include "fontwrapper.hpp" + namespace Gui { /** * @brief A variant of the EditBox that only allows integer inputs */ - class NumericEditBox : public MyGUI::EditBox + class NumericEditBox : public FontWrapper { MYGUI_RTTI_DERIVED(NumericEditBox) @@ -17,7 +19,8 @@ namespace Gui NumericEditBox() : mValue(0), mMinValue(std::numeric_limits::min()), mMaxValue(std::numeric_limits::max()) - {} + { + } void initialiseOverride(); void shutdownOverride(); diff --git a/components/widgets/sharedstatebutton.cpp b/components/widgets/sharedstatebutton.cpp index 6859a3065..f4456275b 100644 --- a/components/widgets/sharedstatebutton.cpp +++ b/components/widgets/sharedstatebutton.cpp @@ -7,7 +7,6 @@ namespace Gui : mIsMousePressed(false) , mIsMouseFocus(false) { - } void SharedStateButton::shutdownOverride() diff --git a/components/widgets/sharedstatebutton.hpp b/components/widgets/sharedstatebutton.hpp index 3d7fbc84e..414349396 100644 --- a/components/widgets/sharedstatebutton.hpp +++ b/components/widgets/sharedstatebutton.hpp @@ -3,6 +3,8 @@ #include +#include "fontwrapper.hpp" + namespace Gui { @@ -11,7 +13,7 @@ namespace Gui typedef std::vector ButtonGroup; /// @brief A button that applies its own state changes to other widgets, to do this you define it as part of a ButtonGroup. - class SharedStateButton : public MyGUI::Button + class SharedStateButton : public FontWrapper { MYGUI_RTTI_DERIVED(SharedStateButton) diff --git a/components/widgets/widgets.cpp b/components/widgets/widgets.cpp index 92f2084df..c1a9a5053 100644 --- a/components/widgets/widgets.cpp +++ b/components/widgets/widgets.cpp @@ -18,9 +18,12 @@ namespace Gui MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); diff --git a/components/widgets/widgets.hpp b/components/widgets/widgets.hpp index d17132135..a5ba84b11 100644 --- a/components/widgets/widgets.hpp +++ b/components/widgets/widgets.hpp @@ -1,9 +1,10 @@ #ifndef OPENMW_COMPONENTS_WIDGETS_H #define OPENMW_COMPONENTS_WIDGETS_H +extern int GuiFontHeight; + namespace Gui { - /// Register all widgets from this component with MyGUI's factory manager. void registerAllWidgets(); diff --git a/docs/source/reference/modding/settings/GUI.rst b/docs/source/reference/modding/settings/GUI.rst index 3040fc48b..3fe943b6f 100644 --- a/docs/source/reference/modding/settings/GUI.rst +++ b/docs/source/reference/modding/settings/GUI.rst @@ -12,6 +12,27 @@ This setting scales the GUI interface windows. A value of 1.0 results in the normal scale. Larger values are useful to increase the scale of the GUI for high resolution displays. This setting can only be configured by editing the settings configuration file. +font size +--------- + +:Type: integer +:Range: 12 to 20 +:Default: 16 + +Allows to specify glyph size for in-game fonts. +Note: default bitmap fonts are supposed to work with 16px size, otherwise glyphs will be blurry. +TrueType fonts do not have this issue. + +ttf resolution +-------------- + +:Type: integer +:Range: > 0 +:Default: 96 + +Allows to specify resolution for in-game TrueType fonts. +Note: actual resolution depends on "scaling factor" setting value, this value is for 1.0 or lower scaling factor. + menu transparency ----------------- diff --git a/files/mygui/core.skin b/files/mygui/core.skin index 9aa566451..ee9135554 100644 --- a/files/mygui/core.skin +++ b/files/mygui/core.skin @@ -2,7 +2,6 @@ - diff --git a/files/mygui/openmw_confirmation_dialog.layout b/files/mygui/openmw_confirmation_dialog.layout index c5eb573a7..246c8aa8f 100644 --- a/files/mygui/openmw_confirmation_dialog.layout +++ b/files/mygui/openmw_confirmation_dialog.layout @@ -13,7 +13,7 @@ - + diff --git a/files/mygui/openmw_enchanting_dialog.layout b/files/mygui/openmw_enchanting_dialog.layout index ed3cfb03e..4738cdc13 100644 --- a/files/mygui/openmw_enchanting_dialog.layout +++ b/files/mygui/openmw_enchanting_dialog.layout @@ -50,7 +50,7 @@ - + diff --git a/files/mygui/openmw_font.xml b/files/mygui/openmw_font.xml index e4037561d..6ffe3017e 100644 --- a/files/mygui/openmw_font.xml +++ b/files/mygui/openmw_font.xml @@ -2,8 +2,6 @@ - - diff --git a/files/mygui/openmw_inventory_window.layout b/files/mygui/openmw_inventory_window.layout index 6221799b5..e935e2f5c 100644 --- a/files/mygui/openmw_inventory_window.layout +++ b/files/mygui/openmw_inventory_window.layout @@ -29,7 +29,7 @@ - + diff --git a/files/mygui/openmw_journal.layout b/files/mygui/openmw_journal.layout index 964b5ea95..0d3a71ab5 100644 --- a/files/mygui/openmw_journal.layout +++ b/files/mygui/openmw_journal.layout @@ -59,8 +59,9 @@ - - + + + diff --git a/files/mygui/openmw_journal.skin.xml b/files/mygui/openmw_journal.skin.xml index 42be2bb62..e9eecba79 100644 --- a/files/mygui/openmw_journal.skin.xml +++ b/files/mygui/openmw_journal.skin.xml @@ -13,11 +13,9 @@ + - - - - + diff --git a/files/mygui/openmw_list.skin.xml b/files/mygui/openmw_list.skin.xml index 5b6f14dd5..9af0e7966 100644 --- a/files/mygui/openmw_list.skin.xml +++ b/files/mygui/openmw_list.skin.xml @@ -127,7 +127,6 @@ - @@ -140,8 +139,6 @@ - - @@ -153,7 +150,6 @@ - diff --git a/files/mygui/openmw_windows.skin.xml b/files/mygui/openmw_windows.skin.xml index a272ae84a..ef618b316 100644 --- a/files/mygui/openmw_windows.skin.xml +++ b/files/mygui/openmw_windows.skin.xml @@ -156,10 +156,10 @@ - + - + @@ -443,7 +443,6 @@ - @@ -458,7 +457,6 @@ ------------------------------------------------------ --> - @@ -593,7 +591,6 @@ - @@ -730,7 +727,6 @@ - diff --git a/files/settings-default.cfg b/files/settings-default.cfg index c2ac2eb1c..45cee0559 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -142,6 +142,12 @@ global = false # Scales GUI window and widget size. (<1.0 is smaller, >1.0 is larger). scaling factor = 1.0 +# Size of in-game fonts +font size = 16 + +# Resolution of TrueType fonts glyphs +ttf resolution = 96 + # Transparency of GUI windows (0.0 to 1.0, transparent to opaque). menu transparency = 0.84 From 8da099713e8c7a7535a2932dfe454013feb16f85 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 1 Sep 2018 22:02:43 +0400 Subject: [PATCH 002/163] Minor layout fixes to fit different font size --- files/mygui/openmw_alchemy_window.layout | 2 +- files/mygui/openmw_chargen_birth.layout | 2 +- files/mygui/openmw_chargen_class.layout | 6 ++-- .../mygui/openmw_chargen_create_class.layout | 6 ++-- ...penmw_chargen_generate_class_result.layout | 2 +- files/mygui/openmw_chargen_race.layout | 16 ++++----- .../openmw_chargen_select_attribute.layout | 31 +++++++++-------- .../mygui/openmw_chargen_select_skill.layout | 33 ++++++++++--------- ...penmw_chargen_select_specialization.layout | 11 ++++--- files/mygui/openmw_container_window.layout | 2 +- files/mygui/openmw_count_window.layout | 6 ++-- files/mygui/openmw_trade_window.layout | 4 +-- 12 files changed, 65 insertions(+), 56 deletions(-) diff --git a/files/mygui/openmw_alchemy_window.layout b/files/mygui/openmw_alchemy_window.layout index a8e5431fb..edc9a4c3d 100644 --- a/files/mygui/openmw_alchemy_window.layout +++ b/files/mygui/openmw_alchemy_window.layout @@ -74,7 +74,7 @@ - + diff --git a/files/mygui/openmw_chargen_birth.layout b/files/mygui/openmw_chargen_birth.layout index a137d83d1..18d412340 100644 --- a/files/mygui/openmw_chargen_birth.layout +++ b/files/mygui/openmw_chargen_birth.layout @@ -16,7 +16,7 @@ > - + diff --git a/files/mygui/openmw_chargen_class.layout b/files/mygui/openmw_chargen_class.layout index 98c8e4fde..1402d9b5c 100644 --- a/files/mygui/openmw_chargen_class.layout +++ b/files/mygui/openmw_chargen_class.layout @@ -27,11 +27,11 @@ - + - + @@ -73,7 +73,7 @@ - + diff --git a/files/mygui/openmw_chargen_create_class.layout b/files/mygui/openmw_chargen_create_class.layout index c04cfe064..fe491eb6d 100644 --- a/files/mygui/openmw_chargen_create_class.layout +++ b/files/mygui/openmw_chargen_create_class.layout @@ -11,7 +11,7 @@ - + @@ -26,7 +26,7 @@ - + @@ -74,7 +74,7 @@ - + diff --git a/files/mygui/openmw_chargen_generate_class_result.layout b/files/mygui/openmw_chargen_generate_class_result.layout index d49f57dde..48a203b18 100644 --- a/files/mygui/openmw_chargen_generate_class_result.layout +++ b/files/mygui/openmw_chargen_generate_class_result.layout @@ -24,7 +24,7 @@ - + diff --git a/files/mygui/openmw_chargen_race.layout b/files/mygui/openmw_chargen_race.layout index 31f0436d5..ea7ec6179 100644 --- a/files/mygui/openmw_chargen_race.layout +++ b/files/mygui/openmw_chargen_race.layout @@ -1,7 +1,7 @@ - + @@ -54,28 +54,28 @@ - + - + - + - + - + - + - + diff --git a/files/mygui/openmw_chargen_select_attribute.layout b/files/mygui/openmw_chargen_select_attribute.layout index 0821d4a57..57bd4ebc6 100644 --- a/files/mygui/openmw_chargen_select_attribute.layout +++ b/files/mygui/openmw_chargen_select_attribute.layout @@ -1,28 +1,31 @@ - - + + - + - - - - - - - - + + + + + + + + - - - + + + + + + diff --git a/files/mygui/openmw_chargen_select_skill.layout b/files/mygui/openmw_chargen_select_skill.layout index d1061882d..3737ea904 100644 --- a/files/mygui/openmw_chargen_select_skill.layout +++ b/files/mygui/openmw_chargen_select_skill.layout @@ -1,10 +1,10 @@ - - + + - + @@ -44,20 +44,23 @@ - - - - - - - - - + + + + + + + + + - - - + + + + + + diff --git a/files/mygui/openmw_chargen_select_specialization.layout b/files/mygui/openmw_chargen_select_specialization.layout index c4fe6c631..47dc32f62 100644 --- a/files/mygui/openmw_chargen_select_specialization.layout +++ b/files/mygui/openmw_chargen_select_specialization.layout @@ -2,7 +2,7 @@ - + @@ -22,9 +22,12 @@ - - - + + + + + + diff --git a/files/mygui/openmw_container_window.layout b/files/mygui/openmw_container_window.layout index 6bb585e50..5783779db 100644 --- a/files/mygui/openmw_container_window.layout +++ b/files/mygui/openmw_container_window.layout @@ -9,7 +9,7 @@ - + diff --git a/files/mygui/openmw_count_window.layout b/files/mygui/openmw_count_window.layout index 520d49fa3..2e083dcea 100644 --- a/files/mygui/openmw_count_window.layout +++ b/files/mygui/openmw_count_window.layout @@ -16,12 +16,12 @@ - + - + - + diff --git a/files/mygui/openmw_trade_window.layout b/files/mygui/openmw_trade_window.layout index 0af9d59fe..30e22302d 100644 --- a/files/mygui/openmw_trade_window.layout +++ b/files/mygui/openmw_trade_window.layout @@ -48,7 +48,7 @@ - + @@ -62,7 +62,7 @@ - + From f89393fd6244e38ff4abed1f7a317d29599dc453 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sun, 2 Sep 2018 18:08:49 +0400 Subject: [PATCH 003/163] Validate 'ttf resolution' option value --- apps/openmw/mwgui/windowmanagerimp.cpp | 2 +- docs/source/reference/modding/settings/GUI.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 493a14a1c..03cbc3ef2 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -309,7 +309,7 @@ namespace MWGui // to allow to configure font size via config file, without need to edit XML file. // Also we should take UI scaling factor in account int resolution = Settings::Manager::getInt("ttf resolution", "GUI"); - resolution = std::max(0, resolution); + resolution = std::min(960, std::max(48, resolution)); float uiScale = Settings::Manager::getFloat("scaling factor", "GUI"); diff --git a/docs/source/reference/modding/settings/GUI.rst b/docs/source/reference/modding/settings/GUI.rst index 3fe943b6f..2092096c9 100644 --- a/docs/source/reference/modding/settings/GUI.rst +++ b/docs/source/reference/modding/settings/GUI.rst @@ -27,7 +27,7 @@ ttf resolution -------------- :Type: integer -:Range: > 0 +:Range: 48 to 960 :Default: 96 Allows to specify resolution for in-game TrueType fonts. From c9c0230d2ac6a2fb8798608cf36c3ec487a9ab27 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Mon, 3 Sep 2018 20:45:04 +0400 Subject: [PATCH 004/163] Scale journal fonts separately from common ones --- apps/openmw/mwgui/bookpage.cpp | 23 +++++-- apps/openmw/mwgui/bookpage.hpp | 2 +- apps/openmw/mwgui/formatting.cpp | 2 +- apps/openmw/mwgui/formatting.hpp | 2 +- apps/openmw/mwgui/windowmanagerimp.cpp | 67 +++++++++++++++---- components/fontloader/fontloader.cpp | 15 +++++ components/sdlutil/sdlvideowrapper.cpp | 5 ++ .../source/reference/modding/settings/GUI.rst | 2 +- files/mygui/openmw_book.layout | 2 + files/mygui/openmw_journal.layout | 2 + files/mygui/openmw_journal.skin.xml | 4 +- 11 files changed, 102 insertions(+), 24 deletions(-) diff --git a/apps/openmw/mwgui/bookpage.cpp b/apps/openmw/mwgui/bookpage.cpp index 8f2e8b5ed..8e264312e 100644 --- a/apps/openmw/mwgui/bookpage.cpp +++ b/apps/openmw/mwgui/bookpage.cpp @@ -264,18 +264,29 @@ struct TypesetBookImpl::Typesetter : BookTypesetter { } - Style * createStyle (char const * fontName, const Colour& fontColour) + Style * createStyle (const std::string& fontName, const Colour& fontColour) { - if (strcmp(fontName, "") == 0) - return createStyle(MyGUI::FontManager::getInstance().getDefaultFont().c_str(), fontColour); + const std::string templateName = "Journalbook "; + std::string bookFont; + if (fontName.empty()) + { + bookFont = MyGUI::FontManager::getInstance().getDefaultFont(); + bookFont = templateName + bookFont; + return createStyle(bookFont, fontColour); + } + + if (fontName.compare(0, templateName.size(), templateName) == 0) + bookFont = fontName; + else + bookFont = templateName + bookFont; for (Styles::iterator i = mBook->mStyles.begin (); i != mBook->mStyles.end (); ++i) - if (i->match (fontName, fontColour, fontColour, fontColour, 0)) + if (i->match (bookFont.c_str(), fontColour, fontColour, fontColour, 0)) return &*i; - MyGUI::IFont* font = MyGUI::FontManager::getInstance().getByName(fontName); + MyGUI::IFont* font = MyGUI::FontManager::getInstance().getByName(bookFont); if (!font) - throw std::runtime_error(std::string("can't find font ") + fontName); + throw std::runtime_error(std::string("can't find font ") + bookFont); StyleImpl & style = *mBook->mStyles.insert (mBook->mStyles.end (), StyleImpl ()); style.mFont = font; diff --git a/apps/openmw/mwgui/bookpage.hpp b/apps/openmw/mwgui/bookpage.hpp index b8174bd3d..c75f00ca4 100644 --- a/apps/openmw/mwgui/bookpage.hpp +++ b/apps/openmw/mwgui/bookpage.hpp @@ -102,7 +102,7 @@ namespace MWGui static Ptr create (int pageWidth, int pageHeight); /// Create a simple text style consisting of a font and a text color. - virtual Style* createStyle (char const * Font, const Colour& Colour) = 0; + virtual Style* createStyle (const std::string& fontName, const Colour& colour) = 0; /// Create a hyper-link style with a user-defined identifier based on an /// existing style. The unique flag forces a new instance of this style diff --git a/apps/openmw/mwgui/formatting.cpp b/apps/openmw/mwgui/formatting.cpp index 157aeba26..fd5ed4faa 100644 --- a/apps/openmw/mwgui/formatting.cpp +++ b/apps/openmw/mwgui/formatting.cpp @@ -377,7 +377,7 @@ namespace MWGui if (attr.find("face") != attr.end()) { std::string face = attr.at("face"); - mTextStyle.mFont = face; + mTextStyle.mFont = "Journalbook "+face; } if (attr.find("size") != attr.end()) { diff --git a/apps/openmw/mwgui/formatting.hpp b/apps/openmw/mwgui/formatting.hpp index 7ccb0824e..6d98d48b3 100644 --- a/apps/openmw/mwgui/formatting.hpp +++ b/apps/openmw/mwgui/formatting.hpp @@ -14,7 +14,7 @@ namespace MWGui { TextStyle() : mColour(0,0,0) - , mFont("Default") + , mFont("") , mTextSize(16) { } diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 03cbc3ef2..f4723da47 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -293,47 +293,90 @@ namespace MWGui void WindowManager::loadFontDelegate(MyGUI::xml::ElementPtr _node, const std::string& _file, MyGUI::Version _version) { - MyGUI::xml::ElementEnumerator root = _node->getElementEnumerator(); - while (root.next("Resource")) + const std::string templateName = "Journalbook "; + MyGUI::xml::ElementEnumerator font = _node->getElementEnumerator(); + bool createCopy = false; + while (font.next("Resource")) { std::string type, name; - root->findAttribute("type", type); - root->findAttribute("name", name); + font->findAttribute("type", type); + font->findAttribute("name", name); if (name.empty()) continue; if (Misc::StringUtils::ciEqual(type, "ResourceTrueTypeFont")) { + createCopy = true; + // For TrueType fonts we should override Size and Resolution properties - // to allow to configure font size via config file, without need to edit XML file. - // Also we should take UI scaling factor in account + // to allow to configure font size via config file, without need to edit XML files. + // Also we should take UI scaling factor in account. int resolution = Settings::Manager::getInt("ttf resolution", "GUI"); resolution = std::min(960, std::max(48, resolution)); float uiScale = Settings::Manager::getFloat("scaling factor", "GUI"); + resolution *= uiScale; - if (uiScale > 1.0f) - resolution *= uiScale; - - MyGUI::xml::ElementPtr resolutionNode = root->createChild("Property"); + MyGUI::xml::ElementPtr resolutionNode = font->createChild("Property"); resolutionNode->addAttribute("key", "Resolution"); resolutionNode->addAttribute("value", std::to_string(resolution)); - MyGUI::xml::ElementPtr sizeNode = root->createChild("Property"); + MyGUI::xml::ElementPtr sizeNode = font->createChild("Property"); sizeNode->addAttribute("key", "Size"); sizeNode->addAttribute("value", std::to_string(mFontHeight)); } else if (Misc::StringUtils::ciEqual(type, "ResourceSkin")) { // We should adjust line height for MyGUI widgets depending on font size - MyGUI::xml::ElementPtr heightNode = root->createChild("Property"); + MyGUI::xml::ElementPtr heightNode = font->createChild("Property"); heightNode->addAttribute("key", "HeightLine"); heightNode->addAttribute("value", std::to_string(mFontHeight+2)); } } MyGUI::ResourceManager::getInstance().loadFromXmlNode(_node, _file, _version); + + if (createCopy) + { + MyGUI::xml::ElementPtr copy = _node->createCopy(); + + MyGUI::xml::ElementEnumerator copyFont = copy->getElementEnumerator(); + while (copyFont.next("Resource")) + { + std::string type, name; + copyFont->findAttribute("type", type); + copyFont->findAttribute("name", name); + + if (name.empty()) + continue; + + if (Misc::StringUtils::ciEqual(type, "ResourceTrueTypeFont")) + { + // Since the journal and books use the custom scaling factor depending on resolution, + // setup separate fonts with different Resolution to fit these windows. + // These fonts have an internal prefix. + int resolution = Settings::Manager::getInt("ttf resolution", "GUI"); + resolution = std::min(960, std::max(48, resolution)); + + float currentX = Settings::Manager::getInt("resolution x", "Video"); + float currentY = Settings::Manager::getInt("resolution y", "Video"); + // TODO: read size from openmw_layout.xml + float heightScale = (currentY / 520); + float widthScale = (currentX / 600); + float uiScale = std::min(widthScale, heightScale); + resolution *= uiScale; + + MyGUI::xml::ElementPtr resolutionNode = copyFont->createChild("Property"); + resolutionNode->addAttribute("key", "Resolution"); + resolutionNode->addAttribute("value", std::to_string(resolution)); + + copyFont->setAttribute("name", "Journalbook " + name); + } + } + + MyGUI::ResourceManager::getInstance().loadFromXmlNode(copy, _file, _version); + } } void WindowManager::initUI() diff --git a/components/fontloader/fontloader.cpp b/components/fontloader/fontloader.cpp index 37214a038..adcdd2d14 100644 --- a/components/fontloader/fontloader.cpp +++ b/components/fontloader/fontloader.cpp @@ -480,6 +480,14 @@ namespace Gui font->deserialization(root, MyGUI::Version(3,2,0)); + // Setup "book" version of font as fallback if we will not use TrueType fonts + MyGUI::ResourceManualFont* bookFont = static_cast( + MyGUI::FactoryManager::getInstance().createObject("Resource", "ResourceManualFont")); + mFonts.push_back(bookFont); + bookFont->deserialization(root, MyGUI::Version(3,2,0)); + bookFont->setResourceName("Journalbook " + resourceName); + + // Remove automatically registered fonts for (std::vector::iterator it = mFonts.begin(); it != mFonts.end();) { if ((*it)->getResourceName() == font->getResourceName()) @@ -487,10 +495,17 @@ namespace Gui MyGUI::ResourceManager::getInstance().removeByName(font->getResourceName()); it = mFonts.erase(it); } + else if ((*it)->getResourceName() == bookFont->getResourceName()) + { + MyGUI::ResourceManager::getInstance().removeByName(bookFont->getResourceName()); + it = mFonts.erase(it); + } else ++it; } + MyGUI::ResourceManager::getInstance().addResource(font); + MyGUI::ResourceManager::getInstance().addResource(bookFont); } } diff --git a/components/sdlutil/sdlvideowrapper.cpp b/components/sdlutil/sdlvideowrapper.cpp index c2963be86..d5c8833ea 100644 --- a/components/sdlutil/sdlvideowrapper.cpp +++ b/components/sdlutil/sdlvideowrapper.cpp @@ -2,6 +2,8 @@ #include +#include + #include #include @@ -89,6 +91,9 @@ namespace SDLUtil SDL_SetWindowSize(mWindow, width, height); SDL_SetWindowBordered(mWindow, windowBorder ? SDL_TRUE : SDL_FALSE); } + + // We should reload TrueType fonts for new resolution + MyGUI::ResourceManager::getInstance().load("openmw_font.xml"); } } diff --git a/docs/source/reference/modding/settings/GUI.rst b/docs/source/reference/modding/settings/GUI.rst index 2092096c9..c8f4e16f8 100644 --- a/docs/source/reference/modding/settings/GUI.rst +++ b/docs/source/reference/modding/settings/GUI.rst @@ -31,7 +31,7 @@ ttf resolution :Default: 96 Allows to specify resolution for in-game TrueType fonts. -Note: actual resolution depends on "scaling factor" setting value, this value is for 1.0 or lower scaling factor. +Note: actual resolution depends on "scaling factor" setting value, this value is for 1.0 scaling factor. menu transparency ----------------- diff --git a/files/mygui/openmw_book.layout b/files/mygui/openmw_book.layout index c83c4982b..e6f0f858c 100644 --- a/files/mygui/openmw_book.layout +++ b/files/mygui/openmw_book.layout @@ -32,11 +32,13 @@ + + diff --git a/files/mygui/openmw_journal.layout b/files/mygui/openmw_journal.layout index 0d3a71ab5..5d6e29066 100644 --- a/files/mygui/openmw_journal.layout +++ b/files/mygui/openmw_journal.layout @@ -25,10 +25,12 @@ + + diff --git a/files/mygui/openmw_journal.skin.xml b/files/mygui/openmw_journal.skin.xml index e9eecba79..0702d8c5e 100644 --- a/files/mygui/openmw_journal.skin.xml +++ b/files/mygui/openmw_journal.skin.xml @@ -2,7 +2,7 @@ - + @@ -19,7 +19,7 @@ - + From 7a986f38da71544f1d9f6965942ba7463c1e7055 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 4 Sep 2018 09:15:07 +0400 Subject: [PATCH 005/163] Support for user-defined TrueType fonts --- apps/openmw/engine.cpp | 2 +- apps/openmw/mwbase/windowmanager.hpp | 2 ++ apps/openmw/mwgui/windowmanagerimp.cpp | 15 +++++++++++---- apps/openmw/mwgui/windowmanagerimp.hpp | 5 +++-- apps/openmw/mwinput/inputmanagerimp.cpp | 3 +++ components/fontloader/fontloader.cpp | 24 ++++++++++++++++++++++-- components/fontloader/fontloader.hpp | 9 +++++++-- components/sdlutil/sdlvideowrapper.cpp | 5 ----- 8 files changed, 49 insertions(+), 16 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 2941ede1b..057113ba3 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -516,7 +516,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) MWGui::WindowManager* window = new MWGui::WindowManager(mViewer, guiRoot, mResourceSystem.get(), mWorkQueue.get(), mCfgMgr.getLogPath().string() + std::string("/"), myguiResources, mScriptConsoleMode, mTranslationDataStorage, mEncoding, mExportFonts, mFallbackMap, - Version::getOpenmwVersionDescription(mResDir.string())); + Version::getOpenmwVersionDescription(mResDir.string()), mCfgMgr.getUserConfigPath().string()); mEnvironment.setWindowManager (window); // Create sound system diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index b40f517f9..fb0be30e4 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -290,6 +290,8 @@ namespace MWBase /// Warning: do not use MyGUI::InputManager::setKeyFocusWidget directly. Instead use this. virtual void setKeyFocusWidget (MyGUI::Widget* widget) = 0; + virtual void loadUserFonts() = 0; + virtual Loading::Listener* getLoadingScreen() = 0; /// Should the cursor be visible? diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index f4723da47..481a9ec00 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -38,6 +38,7 @@ #include #include +#include #include #include #include @@ -131,8 +132,8 @@ namespace MWGui WindowManager::WindowManager( osgViewer::Viewer* viewer, osg::Group* guiRoot, Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue, - const std::string& logpath, const std::string& resourcePath, bool consoleOnlyScripts, - Translation::Storage& translationDataStorage, ToUTF8::FromType encoding, bool exportFonts, const std::map& fallbackMap, const std::string& versionDescription) + const std::string& logpath, const std::string& resourcePath, bool consoleOnlyScripts, Translation::Storage& translationDataStorage, + ToUTF8::FromType encoding, bool exportFonts, const std::map& fallbackMap, const std::string& versionDescription, const std::string& userDataPath) : mStore(NULL) , mResourceSystem(resourceSystem) , mWorkQueue(workQueue) @@ -210,8 +211,8 @@ namespace MWGui MyGUI::LanguageManager::getInstance().eventRequestTag = MyGUI::newDelegate(this, &WindowManager::onRetrieveTag); // Load fonts - mFontLoader.reset(new Gui::FontLoader(encoding, resourceSystem->getVFS())); - mFontLoader->loadAllFonts(exportFonts); + mFontLoader.reset(new Gui::FontLoader(encoding, resourceSystem->getVFS(), userDataPath)); + mFontLoader->loadBitmapFonts(exportFonts); //Register own widgets with MyGUI MyGUI::FactoryManager::getInstance().registerFactory("Widget"); @@ -245,6 +246,7 @@ namespace MWGui MyGUI::FactoryManager::getInstance().registerFactory("Resource", "ResourceImageSetPointer"); MyGUI::ResourceManager::getInstance().load("core.xml"); + loadUserFonts(); bool keyboardNav = Settings::Manager::getBool("keyboard navigation", "GUI"); mKeyboardNavigation.reset(new KeyboardNavigation()); @@ -379,6 +381,11 @@ namespace MWGui } } + void WindowManager::loadUserFonts() + { + mFontLoader->loadTrueTypeFonts(); + } + void WindowManager::initUI() { // Get size info from the Gui object diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index f3bf6d00f..547701496 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -131,14 +131,15 @@ namespace MWGui typedef std::vector FactionList; WindowManager(osgViewer::Viewer* viewer, osg::Group* guiRoot, Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue, - const std::string& logpath, const std::string& cacheDir, bool consoleOnlyScripts, - Translation::Storage& translationDataStorage, ToUTF8::FromType encoding, bool exportFonts, const std::map& fallbackMap, const std::string& versionDescription); + const std::string& logpath, const std::string& cacheDir, bool consoleOnlyScripts, Translation::Storage& translationDataStorage, + ToUTF8::FromType encoding, bool exportFonts, const std::map& fallbackMap, const std::string& versionDescription, const std::string& localPath); virtual ~WindowManager(); /// Set the ESMStore to use for retrieving of GUI-related strings. void setStore (const MWWorld::ESMStore& store); void initUI(); + virtual void loadUserFonts(); virtual Loading::Listener* getLoadingScreen(); diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index b24c8cc14..ec9ead595 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -676,6 +676,9 @@ namespace MWInput Settings::Manager::getInt("resolution y", "Video"), Settings::Manager::getBool("fullscreen", "Video"), Settings::Manager::getBool("window border", "Video")); + + // We should reload TrueType fonts to fit new resolution + MWBase::Environment::get().getWindowManager()->loadUserFonts(); } } diff --git a/components/fontloader/fontloader.cpp b/components/fontloader/fontloader.cpp index adcdd2d14..0378e294e 100644 --- a/components/fontloader/fontloader.cpp +++ b/components/fontloader/fontloader.cpp @@ -145,8 +145,9 @@ namespace namespace Gui { - FontLoader::FontLoader(ToUTF8::FromType encoding, const VFS::Manager* vfs) + FontLoader::FontLoader(ToUTF8::FromType encoding, const VFS::Manager* vfs, const std::string& userDataPath) : mVFS(vfs) + , mUserDataPath(userDataPath) { if (encoding == ToUTF8::WINDOWS_1252) mEncoding = ToUTF8::CP437; @@ -175,7 +176,7 @@ namespace Gui mFonts.clear(); } - void FontLoader::loadAllFonts(bool exportToFile) + void FontLoader::loadBitmapFonts(bool exportToFile) { const std::map& index = mVFS->getIndex(); @@ -198,6 +199,25 @@ namespace Gui } } + void FontLoader::loadTrueTypeFonts() + { + osgMyGUI::DataManager* dataManager = dynamic_cast(&osgMyGUI::DataManager::getInstance()); + if (!dataManager) + { + Log(Debug::Error) << "Can not load TrueType fonts: osgMyGUI::DataManager is not available."; + return; + } + + const std::string cfg = dataManager->getDataPath(""); + const std::string fontFile = mUserDataPath + "/" + "Fonts" + "/" + "openmw_font.xml"; + if (!boost::filesystem::exists(fontFile)) + return; + + dataManager->setResourcePath(mUserDataPath + "/" + "Fonts"); + MyGUI::ResourceManager::getInstance().load("openmw_font.xml"); + dataManager->setResourcePath(cfg); + } + typedef struct { diff --git a/components/fontloader/fontloader.hpp b/components/fontloader/fontloader.hpp index b92815f13..39301f9f5 100644 --- a/components/fontloader/fontloader.hpp +++ b/components/fontloader/fontloader.hpp @@ -1,6 +1,9 @@ #ifndef OPENMW_COMPONENTS_FONTLOADER_H #define OPENMW_COMPONENTS_FONTLOADER_H +#include "boost/filesystem/operations.hpp" + +#include #include namespace VFS @@ -23,15 +26,17 @@ namespace Gui class FontLoader { public: - FontLoader (ToUTF8::FromType encoding, const VFS::Manager* vfs); + FontLoader (ToUTF8::FromType encoding, const VFS::Manager* vfs, const std::string& userDataPath); ~FontLoader(); /// @param exportToFile export the converted fonts (Images and XML with glyph metrics) to files? - void loadAllFonts (bool exportToFile); + void loadBitmapFonts (bool exportToFile); + void loadTrueTypeFonts (); private: ToUTF8::FromType mEncoding; const VFS::Manager* mVFS; + std::string mUserDataPath; std::vector mTextures; std::vector mFonts; diff --git a/components/sdlutil/sdlvideowrapper.cpp b/components/sdlutil/sdlvideowrapper.cpp index d5c8833ea..c2963be86 100644 --- a/components/sdlutil/sdlvideowrapper.cpp +++ b/components/sdlutil/sdlvideowrapper.cpp @@ -2,8 +2,6 @@ #include -#include - #include #include @@ -91,9 +89,6 @@ namespace SDLUtil SDL_SetWindowSize(mWindow, width, height); SDL_SetWindowBordered(mWindow, windowBorder ? SDL_TRUE : SDL_FALSE); } - - // We should reload TrueType fonts for new resolution - MyGUI::ResourceManager::getInstance().load("openmw_font.xml"); } } From e300a16b24a0f8881549f4c97afb86466ef4612b Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 4 Sep 2018 12:19:50 +0400 Subject: [PATCH 006/163] Use field for columns count instead of out integer --- apps/openmw/mwgui/journalbooks.cpp | 16 ++++++++-------- apps/openmw/mwgui/journalbooks.hpp | 7 ++++--- apps/openmw/mwgui/journalwindow.cpp | 5 ++--- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/apps/openmw/mwgui/journalbooks.cpp b/apps/openmw/mwgui/journalbooks.cpp index 1956ee1c2..4302740f6 100644 --- a/apps/openmw/mwgui/journalbooks.cpp +++ b/apps/openmw/mwgui/journalbooks.cpp @@ -157,7 +157,7 @@ MWGui::BookTypesetter::Utf8Span to_utf8_span (char const * text) typedef TypesetBook::Ptr book; JournalBooks::JournalBooks (JournalViewModel::Ptr model, ToUTF8::FromType encoding) : - mModel (model), mEncoding(encoding) + mModel (model), mEncoding(encoding), mIndexPagesCount(0) { } @@ -218,23 +218,23 @@ book JournalBooks::createQuestBook (const std::string& questName) return typesetter->complete (); } -book JournalBooks::createTopicIndexBook (int& columnsCount) +book JournalBooks::createTopicIndexBook () { bool isRussian = (mEncoding == ToUTF8::WINDOWS_1251); - BookTypesetter::Ptr typesetter = isRussian ? createCyrillicJournalIndex(columnsCount) : createLatinJournalIndex(columnsCount); + BookTypesetter::Ptr typesetter = isRussian ? createCyrillicJournalIndex() : createLatinJournalIndex(); return typesetter->complete (); } -BookTypesetter::Ptr JournalBooks::createLatinJournalIndex (int& columnsCount) +BookTypesetter::Ptr JournalBooks::createLatinJournalIndex () { BookTypesetter::Ptr typesetter = BookTypesetter::create (92, 260); typesetter->setSectionAlignment (BookTypesetter::AlignCenter); // Latin journal index always has two columns for now. - columnsCount = 2; + mIndexPagesCount = 2; char ch = 'A'; @@ -261,7 +261,7 @@ BookTypesetter::Ptr JournalBooks::createLatinJournalIndex (int& columnsCount) return typesetter; } -BookTypesetter::Ptr JournalBooks::createCyrillicJournalIndex (int& columnsCount) +BookTypesetter::Ptr JournalBooks::createCyrillicJournalIndex () { BookTypesetter::Ptr typesetter = BookTypesetter::create (92, 260); @@ -273,11 +273,11 @@ BookTypesetter::Ptr JournalBooks::createCyrillicJournalIndex (int& columnsCount) // for small font size split alphabet to two columns (2x15 characers), for big font size split it to three colums (3x10 characters). int sectionBreak = 10; - columnsCount = 3; + mIndexPagesCount = 3; if (fontHeight < 18) { sectionBreak = 15; - columnsCount = 2; + mIndexPagesCount = 2; } unsigned char ch[2] = {0xd0, 0x90}; // CYRILLIC CAPITAL A is a 0xd090 in UTF-8 diff --git a/apps/openmw/mwgui/journalbooks.hpp b/apps/openmw/mwgui/journalbooks.hpp index cc6f42cc8..05eda6e22 100644 --- a/apps/openmw/mwgui/journalbooks.hpp +++ b/apps/openmw/mwgui/journalbooks.hpp @@ -22,14 +22,15 @@ namespace MWGui Book createTopicBook (uintptr_t topicId); Book createTopicBook (const std::string& topicId); Book createQuestBook (const std::string& questName); - Book createTopicIndexBook (int& columnsCount); + Book createTopicIndexBook (); ToUTF8::FromType mEncoding; + int mIndexPagesCount; private: BookTypesetter::Ptr createTypesetter (); - BookTypesetter::Ptr createLatinJournalIndex (int& columnsCount); - BookTypesetter::Ptr createCyrillicJournalIndex (int& columnsCount); + BookTypesetter::Ptr createLatinJournalIndex (); + BookTypesetter::Ptr createCyrillicJournalIndex (); }; } diff --git a/apps/openmw/mwgui/journalwindow.cpp b/apps/openmw/mwgui/journalwindow.cpp index cef20159d..a2f6ea142 100644 --- a/apps/openmw/mwgui/journalwindow.cpp +++ b/apps/openmw/mwgui/journalwindow.cpp @@ -465,11 +465,10 @@ namespace { setOptionsMode (); - int pagesCount; if (!mTopicIndexBook) - mTopicIndexBook = createTopicIndexBook (pagesCount); + mTopicIndexBook = createTopicIndexBook (); - if (pagesCount == 3) + if (mIndexPagesCount == 3) { getPage (LeftTopicIndex)->showPage (mTopicIndexBook, 0); getPage (CenterTopicIndex)->showPage (mTopicIndexBook, 1); From 363516049d449d21c98f161705894192e858a112 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Fri, 14 Sep 2018 18:37:04 +0300 Subject: [PATCH 007/163] Actually fix General number formatting --- components/interpreter/miscopcodes.hpp | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/components/interpreter/miscopcodes.hpp b/components/interpreter/miscopcodes.hpp index 03b7e186f..77e5a0079 100644 --- a/components/interpreter/miscopcodes.hpp +++ b/components/interpreter/miscopcodes.hpp @@ -65,23 +65,18 @@ namespace Interpreter } else if (notation == ShortestNotation) { - std::string scientific; - std::string fixed; - - out << std::scientific << value; - - scientific = out.str(); + out << value; + std::string standard = out.str(); out.str(std::string()); out.clear(); - out << std::fixed << value; + out << std::scientific << value; + std::string scientific = out.str(); - fixed = out.str(); - - mFormattedMessage += fixed.length() < scientific.length() ? fixed : scientific; + mFormattedMessage += standard.length() < scientific.length() ? standard : scientific; } - else + else { out << std::scientific << value; mFormattedMessage += out.str(); From d3aa5840ec380fcb029e795f31f7158f46d195a7 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Fri, 24 Aug 2018 16:57:32 +0300 Subject: [PATCH 008/163] Refactor magic effect record verifying --- apps/opencs/model/tools/magiceffectcheck.cpp | 93 +++++++------------- apps/opencs/model/tools/magiceffectcheck.hpp | 11 +-- 2 files changed, 36 insertions(+), 68 deletions(-) diff --git a/apps/opencs/model/tools/magiceffectcheck.cpp b/apps/opencs/model/tools/magiceffectcheck.cpp index 531bd9e1d..448e4d28c 100644 --- a/apps/opencs/model/tools/magiceffectcheck.cpp +++ b/apps/opencs/model/tools/magiceffectcheck.cpp @@ -9,7 +9,7 @@ namespace { - void addMessageIfNotEmpty(CSMDoc::Messages &messages, const CSMWorld::UniversalId &id, const std::string& text) + void addMessage(CSMDoc::Messages &messages, const CSMWorld::UniversalId &id, const std::string& text) { if (!text.empty()) { @@ -18,42 +18,34 @@ namespace } } -bool CSMTools::MagicEffectCheckStage::isTextureExists(const std::string &texture, bool isIcon) const +std::string CSMTools::MagicEffectCheckStage::checkTexture(const std::string &texture, bool isIcon) const { + if (texture.empty()) return (isIcon ? "Icon is not specified" : std::string()); + const CSMWorld::Resources &textures = isIcon ? mIcons : mTextures; - bool exists = false; + if (textures.searchId(texture) != -1) return std::string(); - if (textures.searchId(texture) != -1) - { - exists = true; - } - else - { - std::string ddsTexture = texture; - if (Misc::ResourceHelpers::changeExtensionToDds(ddsTexture) && textures.searchId(ddsTexture) != -1) - { - exists = true; - } - } + std::string ddsTexture = texture; + if (Misc::ResourceHelpers::changeExtensionToDds(ddsTexture) && textures.searchId(ddsTexture) != -1) return std::string(); - return exists; + return (isIcon ? "Icon '" : "Particle '") + texture + "' does not exist"; } -std::string CSMTools::MagicEffectCheckStage::checkReferenceable(const std::string &id, +std::string CSMTools::MagicEffectCheckStage::checkObject(const std::string &id, const CSMWorld::UniversalId &type, const std::string &column) const { std::string error; if (!id.empty()) { - CSMWorld::RefIdData::LocalIndex index = mReferenceables.getDataSet().searchId(id); + CSMWorld::RefIdData::LocalIndex index = mObjects.getDataSet().searchId(id); if (index.first == -1) { - error = "No such " + column + " '" + id + "'"; + error = column + " '" + id + "' " + "does not exist"; } else if (index.second != type.getType()) { - error = column + " is not of type " + type.getTypeName(); + error = column + " '" + id + "' " + "does not have " + type.getTypeName() + " type"; } } return error; @@ -64,19 +56,19 @@ std::string CSMTools::MagicEffectCheckStage::checkSound(const std::string &id, c std::string error; if (!id.empty() && mSounds.searchId(id) == -1) { - error = "No such " + column + " '" + id + "'"; + error = column + " '" + id + "' " + "does not exist"; } return error; } CSMTools::MagicEffectCheckStage::MagicEffectCheckStage(const CSMWorld::IdCollection &effects, const CSMWorld::IdCollection &sounds, - const CSMWorld::RefIdCollection &referenceables, + const CSMWorld::RefIdCollection &objects, const CSMWorld::Resources &icons, const CSMWorld::Resources &textures) : mMagicEffects(effects), mSounds(sounds), - mReferenceables(referenceables), + mObjects(objects), mIcons(icons), mTextures(textures) { @@ -100,46 +92,25 @@ void CSMTools::MagicEffectCheckStage::perform(int stage, CSMDoc::Messages &messa ESM::MagicEffect effect = record.get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_MagicEffect, effect.mId); - - if (effect.mData.mBaseCost < 0.0f) - { - messages.push_back(std::make_pair(id, "Base Cost is negative")); - } - - if (effect.mIcon.empty()) - { - messages.push_back(std::make_pair(id, "Icon is not specified")); - } - else if (!isTextureExists(effect.mIcon, true)) - { - messages.push_back(std::make_pair(id, "No such Icon '" + effect.mIcon + "'")); - } - - if (!effect.mParticle.empty() && !isTextureExists(effect.mParticle, false)) - { - messages.push_back(std::make_pair(id, "No such Particle '" + effect.mParticle + "'")); - } - - addMessageIfNotEmpty(messages, - id, - checkReferenceable(effect.mCasting, CSMWorld::UniversalId::Type_Static, "Casting Object")); - addMessageIfNotEmpty(messages, - id, - checkReferenceable(effect.mHit, CSMWorld::UniversalId::Type_Static, "Hit Object")); - addMessageIfNotEmpty(messages, - id, - checkReferenceable(effect.mArea, CSMWorld::UniversalId::Type_Static, "Area Object")); - addMessageIfNotEmpty(messages, - id, - checkReferenceable(effect.mBolt, CSMWorld::UniversalId::Type_Weapon, "Bolt Object")); - - addMessageIfNotEmpty(messages, id, checkSound(effect.mCastSound, "Casting Sound")); - addMessageIfNotEmpty(messages, id, checkSound(effect.mHitSound, "Hit Sound")); - addMessageIfNotEmpty(messages, id, checkSound(effect.mAreaSound, "Area Sound")); - addMessageIfNotEmpty(messages, id, checkSound(effect.mBoltSound, "Bolt Sound")); if (effect.mDescription.empty()) { - messages.push_back(std::make_pair(id, "Description is empty")); + addMessage(messages, id, "Description is missing"); } + + if (effect.mData.mBaseCost < 0.0f) + { + addMessage(messages, id, "Base cost is negative"); + } + + addMessage(messages, id, checkTexture(effect.mIcon, true)); + addMessage(messages, id, checkTexture(effect.mParticle, false)); + addMessage(messages, id, checkObject(effect.mCasting, CSMWorld::UniversalId::Type_Static, "Casting object")); + addMessage(messages, id, checkObject(effect.mHit, CSMWorld::UniversalId::Type_Static, "Hit object")); + addMessage(messages, id, checkObject(effect.mArea, CSMWorld::UniversalId::Type_Static, "Area object")); + addMessage(messages, id, checkObject(effect.mBolt, CSMWorld::UniversalId::Type_Weapon, "Bolt object")); + addMessage(messages, id, checkSound(effect.mCastSound, "Casting sound")); + addMessage(messages, id, checkSound(effect.mHitSound, "Hit sound")); + addMessage(messages, id, checkSound(effect.mAreaSound, "Area sound")); + addMessage(messages, id, checkSound(effect.mBoltSound, "Bolt sound")); } diff --git a/apps/opencs/model/tools/magiceffectcheck.hpp b/apps/opencs/model/tools/magiceffectcheck.hpp index 28a406283..3e45495c1 100644 --- a/apps/opencs/model/tools/magiceffectcheck.hpp +++ b/apps/opencs/model/tools/magiceffectcheck.hpp @@ -21,23 +21,20 @@ namespace CSMTools { const CSMWorld::IdCollection &mMagicEffects; const CSMWorld::IdCollection &mSounds; - const CSMWorld::RefIdCollection &mReferenceables; + const CSMWorld::RefIdCollection &mObjects; const CSMWorld::Resources &mIcons; const CSMWorld::Resources &mTextures; bool mIgnoreBaseRecords; private: - bool isTextureExists(const std::string &texture, bool isIcon) const; - - std::string checkReferenceable(const std::string &id, - const CSMWorld::UniversalId &type, - const std::string &column) const; + std::string checkTexture(const std::string &texture, bool isIcon) const; + std::string checkObject(const std::string &id, const CSMWorld::UniversalId &type, const std::string &column) const; std::string checkSound(const std::string &id, const std::string &column) const; public: MagicEffectCheckStage(const CSMWorld::IdCollection &effects, const CSMWorld::IdCollection &sounds, - const CSMWorld::RefIdCollection &referenceables, + const CSMWorld::RefIdCollection &objects, const CSMWorld::Resources &icons, const CSMWorld::Resources &textures); From 51fdb94e34427ca01a70228c41791f7e2e49684b Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Fri, 24 Aug 2018 17:37:07 +0300 Subject: [PATCH 009/163] Add texture check to birthsign verifier --- apps/opencs/model/tools/birthsigncheck.cpp | 42 +++++++++++++++++----- apps/opencs/model/tools/birthsigncheck.hpp | 14 ++++++-- apps/opencs/model/tools/tools.cpp | 2 +- 3 files changed, 46 insertions(+), 12 deletions(-) diff --git a/apps/opencs/model/tools/birthsigncheck.cpp b/apps/opencs/model/tools/birthsigncheck.cpp index fc2989307..4ef264f65 100644 --- a/apps/opencs/model/tools/birthsigncheck.cpp +++ b/apps/opencs/model/tools/birthsigncheck.cpp @@ -4,13 +4,41 @@ #include #include +#include #include "../prefs/state.hpp" +#include "../world/data.hpp" +#include "../world/resources.hpp" #include "../world/universalid.hpp" -CSMTools::BirthsignCheckStage::BirthsignCheckStage (const CSMWorld::IdCollection& birthsigns) -: mBirthsigns (birthsigns) +namespace +{ + void addMessage(CSMDoc::Messages &messages, const CSMWorld::UniversalId &id, const std::string& text) + { + if (!text.empty()) + { + messages.push_back(std::make_pair(id, text)); + } + } +} + + +std::string CSMTools::BirthsignCheckStage::checkTexture(const std::string &texture) const +{ + if (texture.empty()) return "Texture is missing"; + if (mTextures.searchId(texture) != -1) return std::string(); + + std::string ddsTexture = texture; + if (Misc::ResourceHelpers::changeExtensionToDds(ddsTexture) && mTextures.searchId(ddsTexture) != -1) return std::string(); + + return "Texture '" + texture + "' does not exist"; +} + +CSMTools::BirthsignCheckStage::BirthsignCheckStage (const CSMWorld::IdCollection& birthsigns, + const CSMWorld::Resources &textures) +: mBirthsigns(birthsigns), + mTextures(textures) { mIgnoreBaseRecords = false; } @@ -34,17 +62,13 @@ void CSMTools::BirthsignCheckStage::perform (int stage, CSMDoc::Messages& messag CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Birthsign, birthsign.mId); - // test for empty name, description and texture if (birthsign.mName.empty()) - messages.push_back (std::make_pair (id, birthsign.mId + " has an empty name")); + addMessage(messages, id, "Name is missing"); if (birthsign.mDescription.empty()) - messages.push_back (std::make_pair (id, birthsign.mId + " has an empty description")); + addMessage(messages, id, "Description is missing"); - if (birthsign.mTexture.empty()) - messages.push_back (std::make_pair (id, birthsign.mId + " is missing a texture")); - - /// \todo test if the texture exists + addMessage(messages, id, checkTexture(birthsign.mTexture)); /// \todo check data members that can't be edited in the table view } diff --git a/apps/opencs/model/tools/birthsigncheck.hpp b/apps/opencs/model/tools/birthsigncheck.hpp index a8a7a2c14..cd22fb6d0 100644 --- a/apps/opencs/model/tools/birthsigncheck.hpp +++ b/apps/opencs/model/tools/birthsigncheck.hpp @@ -7,17 +7,27 @@ #include "../doc/stage.hpp" +namespace CSMWorld +{ + class Resources; +} + namespace CSMTools { /// \brief VerifyStage: make sure that birthsign records are internally consistent class BirthsignCheckStage : public CSMDoc::Stage { - const CSMWorld::IdCollection& mBirthsigns; + const CSMWorld::IdCollection &mBirthsigns; + const CSMWorld::Resources &mTextures; bool mIgnoreBaseRecords; + private: + std::string checkTexture(const std::string &texture) const; + public: - BirthsignCheckStage (const CSMWorld::IdCollection& birthsigns); + BirthsignCheckStage (const CSMWorld::IdCollection &birthsigns, + const CSMWorld::Resources &textures); virtual int setup(); ///< \return number of steps diff --git a/apps/opencs/model/tools/tools.cpp b/apps/opencs/model/tools/tools.cpp index 445db53af..0946225cf 100644 --- a/apps/opencs/model/tools/tools.cpp +++ b/apps/opencs/model/tools/tools.cpp @@ -78,7 +78,7 @@ CSMDoc::OperationHolder *CSMTools::Tools::getVerifier() mVerifierOperation->appendStage (new RegionCheckStage (mData.getRegions())); - mVerifierOperation->appendStage (new BirthsignCheckStage (mData.getBirthsigns())); + mVerifierOperation->appendStage (new BirthsignCheckStage (mData.getBirthsigns(), mData.getResources (CSMWorld::UniversalId::Type_Textures))); mVerifierOperation->appendStage (new SpellCheckStage (mData.getSpells())); From 5249492a31f31f04d2acd16c691af82df61f3f05 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Fri, 24 Aug 2018 20:13:20 +0300 Subject: [PATCH 010/163] Update skill record verifier messages --- apps/opencs/model/tools/skillcheck.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/opencs/model/tools/skillcheck.cpp b/apps/opencs/model/tools/skillcheck.cpp index b34d18e2a..a2bf3ff03 100644 --- a/apps/opencs/model/tools/skillcheck.cpp +++ b/apps/opencs/model/tools/skillcheck.cpp @@ -38,11 +38,11 @@ void CSMTools::SkillCheckStage::perform (int stage, CSMDoc::Messages& messages) { std::ostringstream stream; - stream << "Use value #" << i << " of " << skill.mId << " is negative"; + stream << "Usage experience value #" << i << " is negative"; messages.push_back (std::make_pair (id, stream.str())); } if (skill.mDescription.empty()) - messages.push_back (std::make_pair (id, skill.mId + " has an empty description")); + messages.push_back (std::make_pair (id, "Description is missing")); } From cf7a8c5775a6dbf29536b6a1c1e7a9c91b6dc9a9 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Fri, 24 Aug 2018 20:20:27 +0300 Subject: [PATCH 011/163] Update soundgen record verifier messages --- apps/opencs/model/tools/soundgencheck.cpp | 12 ++++++------ apps/opencs/model/tools/soundgencheck.hpp | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/opencs/model/tools/soundgencheck.cpp b/apps/opencs/model/tools/soundgencheck.cpp index 3692259ce..99a8c184c 100644 --- a/apps/opencs/model/tools/soundgencheck.cpp +++ b/apps/opencs/model/tools/soundgencheck.cpp @@ -9,10 +9,10 @@ CSMTools::SoundGenCheckStage::SoundGenCheckStage(const CSMWorld::IdCollection &soundGens, const CSMWorld::IdCollection &sounds, - const CSMWorld::RefIdCollection &referenceables) + const CSMWorld::RefIdCollection &objects) : mSoundGens(soundGens), mSounds(sounds), - mReferenceables(referenceables) + mObjects(objects) { mIgnoreBaseRecords = false; } @@ -37,10 +37,10 @@ void CSMTools::SoundGenCheckStage::perform(int stage, CSMDoc::Messages &messages if (!soundGen.mCreature.empty()) { - CSMWorld::RefIdData::LocalIndex creatureIndex = mReferenceables.getDataSet().searchId(soundGen.mCreature); + CSMWorld::RefIdData::LocalIndex creatureIndex = mObjects.getDataSet().searchId(soundGen.mCreature); if (creatureIndex.first == -1) { - messages.push_back(std::make_pair(id, "No such creature '" + soundGen.mCreature + "'")); + messages.push_back(std::make_pair(id, "Creature '" + soundGen.mCreature + "' doesn't exist")); } else if (creatureIndex.second != CSMWorld::UniversalId::Type_Creature) { @@ -50,10 +50,10 @@ void CSMTools::SoundGenCheckStage::perform(int stage, CSMDoc::Messages &messages if (soundGen.mSound.empty()) { - messages.push_back(std::make_pair(id, "Sound is not specified")); + messages.push_back(std::make_pair(id, "Sound is missing")); } else if (mSounds.searchId(soundGen.mSound) == -1) { - messages.push_back(std::make_pair(id, "No such sound '" + soundGen.mSound + "'")); + messages.push_back(std::make_pair(id, "Sound '" + soundGen.mSound + "' doesn't exist")); } } diff --git a/apps/opencs/model/tools/soundgencheck.hpp b/apps/opencs/model/tools/soundgencheck.hpp index 19388cb91..3c2a7f071 100644 --- a/apps/opencs/model/tools/soundgencheck.hpp +++ b/apps/opencs/model/tools/soundgencheck.hpp @@ -12,13 +12,13 @@ namespace CSMTools { const CSMWorld::IdCollection &mSoundGens; const CSMWorld::IdCollection &mSounds; - const CSMWorld::RefIdCollection &mReferenceables; + const CSMWorld::RefIdCollection &mObjects; bool mIgnoreBaseRecords; public: SoundGenCheckStage(const CSMWorld::IdCollection &soundGens, const CSMWorld::IdCollection &sounds, - const CSMWorld::RefIdCollection &referenceables); + const CSMWorld::RefIdCollection &objects); virtual int setup(); ///< \return number of steps From fd1a3ad88d1922246e19e667a1a5aac789fff078 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Fri, 24 Aug 2018 21:02:42 +0300 Subject: [PATCH 012/163] Update object and script record verifier messages --- .../opencs/model/tools/referenceablecheck.cpp | 164 ++++++++---------- apps/opencs/model/tools/scriptcheck.cpp | 9 +- apps/opencs/model/tools/soundcheck.cpp | 2 +- 3 files changed, 80 insertions(+), 95 deletions(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index 3e8dc1188..6d06cce12 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -272,7 +272,7 @@ void CSMTools::ReferenceableCheckStage::activatorCheck( //Checking for model, IIRC all activators should have a model if (activator.mModel.empty()) - messages.push_back (std::make_pair (id, activator.mId + " has no model")); + messages.push_back (std::make_pair (id, "Model is missing")); // ADD CHECK HERE // Check that mentioned scripts exist scriptCheck(activator, messages, id.toString()); @@ -340,11 +340,11 @@ void CSMTools::ReferenceableCheckStage::armorCheck( //checking for armor class, armor should have poistive armor class, but 0 is considered legal if (armor.mData.mArmor < 0) - messages.push_back (std::make_pair (id, armor.mId + " has negative armor class")); + messages.push_back (std::make_pair (id, "Armor class is negative")); //checking for health. Only positive numbers are allowed, or 0 is illegal if (armor.mData.mHealth <= 0) - messages.push_back (std::make_pair (id, armor.mId + " has non positive health")); + messages.push_back (std::make_pair (id, "Durability is non-positive")); // Check that mentioned scripts exist scriptCheck(armor, messages, id.toString()); @@ -385,16 +385,15 @@ void CSMTools::ReferenceableCheckStage::containerCheck( //Checking for model, IIRC all containers should have a model if (container.mModel.empty()) - messages.push_back (std::make_pair (id, container.mId + " has no model")); + messages.push_back (std::make_pair (id, "Model is missing")); // ADD CHECK HERE //Checking for capacity (weight) if (container.mWeight < 0) //0 is allowed - messages.push_back (std::make_pair (id, - container.mId + " has negative weight (capacity)")); + messages.push_back (std::make_pair (id, "Capacity is negative")); //checking for name if (container.mName.empty()) - messages.push_back (std::make_pair (id, container.mId + " has an empty name")); + messages.push_back (std::make_pair (id, "Name is missing")); //checking contained items inventoryListCheck(container.mInventory.mList, messages, id.toString()); @@ -417,61 +416,60 @@ void CSMTools::ReferenceableCheckStage::creatureCheck ( CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Creature, creature.mId); if (creature.mModel.empty()) - messages.push_back (std::make_pair (id, creature.mId + " has no model")); + messages.push_back (std::make_pair (id, "Model is missing")); // ADD CHECK HERE if (creature.mName.empty()) - messages.push_back (std::make_pair (id, creature.mId + " has an empty name")); + messages.push_back (std::make_pair (id, "Name is missing")); //stats checks if (creature.mData.mLevel < 1) - messages.push_back (std::make_pair (id, creature.mId + " has non-positive level")); + messages.push_back (std::make_pair (id, "Level is non-positive")); if (creature.mData.mStrength < 0) - messages.push_back (std::make_pair (id, creature.mId + " has negative strength")); + messages.push_back (std::make_pair (id, "Strength is negative")); if (creature.mData.mIntelligence < 0) - messages.push_back (std::make_pair (id, creature.mId + " has negative intelligence")); + messages.push_back (std::make_pair (id, "Intelligence is negative")); if (creature.mData.mWillpower < 0) - messages.push_back (std::make_pair (id, creature.mId + " has negative willpower")); + messages.push_back (std::make_pair (id, "Willpower is negative")); if (creature.mData.mAgility < 0) - messages.push_back (std::make_pair (id, creature.mId + " has negative agility")); + messages.push_back (std::make_pair (id, "Agility is negative")); if (creature.mData.mSpeed < 0) - messages.push_back (std::make_pair (id, creature.mId + " has negative speed")); + messages.push_back (std::make_pair (id, "Speed is negative")); if (creature.mData.mEndurance < 0) - messages.push_back (std::make_pair (id, creature.mId + " has negative endurance")); + messages.push_back (std::make_pair (id, "Endurance is negative")); if (creature.mData.mPersonality < 0) - messages.push_back (std::make_pair (id, creature.mId + " has negative personality")); + messages.push_back (std::make_pair (id, "Personality is negative")); if (creature.mData.mLuck < 0) - messages.push_back (std::make_pair (id, creature.mId + " has negative luck")); + messages.push_back (std::make_pair (id, "Luck is negative")); if (creature.mData.mHealth < 0) - messages.push_back (std::make_pair (id, creature.mId + " has negative health")); + messages.push_back (std::make_pair (id, "Health is negative")); if (creature.mData.mSoul < 0) - messages.push_back (std::make_pair (id, creature.mId + " has negative soul value")); + messages.push_back (std::make_pair (id, "Soul value is negative")); for (int i = 0; i < 6; ++i) { if (creature.mData.mAttack[i] < 0) { - messages.push_back (std::make_pair (id, - creature.mId + " has negative attack strength")); + messages.push_back (std::make_pair (id, "One of attacks has negative damage")); break; } } //TODO, find meaning of other values - if (creature.mData.mGold < 0) //It seems that this is for gold in merchant creatures - messages.push_back (std::make_pair (id, creature.mId + " has negative gold ")); + if (creature.mData.mGold < 0) + messages.push_back (std::make_pair (id, "Gold count is negative")); if (creature.mScale == 0) - messages.push_back (std::make_pair (id, creature.mId + " has zero scale value")); + messages.push_back (std::make_pair (id, "Scale is equal to zero")); // Check inventory inventoryListCheck(creature.mInventory.mList, messages, id.toString()); @@ -495,10 +493,10 @@ void CSMTools::ReferenceableCheckStage::doorCheck( //usual, name or model if (door.mName.empty()) - messages.push_back (std::make_pair (id, door.mId + " has an empty name")); + messages.push_back (std::make_pair (id, "Name is missing")); if (door.mModel.empty()) - messages.push_back (std::make_pair (id, door.mId + " has no model")); + messages.push_back (std::make_pair (id, "Model is missing")); // ADD CHECK HERE // Check that mentioned scripts exist scriptCheck(door, messages, id.toString()); @@ -572,7 +570,7 @@ void CSMTools::ReferenceableCheckStage::lightCheck( CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Light, light.mId); if (light.mData.mRadius < 0) - messages.push_back (std::make_pair (id, light.mId + " has negative light radius")); + messages.push_back (std::make_pair (id, "Light radius is negative")); if (light.mData.mFlags & ESM::Light::Carry) inventoryItemCheck(light, messages, id.toString()); @@ -667,71 +665,69 @@ void CSMTools::ReferenceableCheckStage::npcCheck ( else { if (npc.mNpdt.mAgility == 0) - messages.push_back (std::make_pair (id, npc.mId + " agility has zero value")); + messages.push_back (std::make_pair (id, "Agility is equal to zero")); if (npc.mNpdt.mEndurance == 0) - messages.push_back (std::make_pair (id, npc.mId + " endurance has zero value")); + messages.push_back (std::make_pair (id, "Endurance is equal to zero")); if (npc.mNpdt.mIntelligence == 0) - messages.push_back (std::make_pair (id, npc.mId + " intelligence has zero value")); + messages.push_back (std::make_pair (id, "Intelligence is equal to zero")); if (npc.mNpdt.mLuck == 0) - messages.push_back (std::make_pair (id, npc.mId + " luck has zero value")); + messages.push_back (std::make_pair (id, "Luck is equal to zero")); if (npc.mNpdt.mPersonality == 0) - messages.push_back (std::make_pair (id, npc.mId + " personality has zero value")); + messages.push_back (std::make_pair (id, "Personality is equal to zero")); if (npc.mNpdt.mStrength == 0) - messages.push_back (std::make_pair (id, npc.mId + " strength has zero value")); + messages.push_back (std::make_pair (id, "Strength is equal to zero")); if (npc.mNpdt.mSpeed == 0) - messages.push_back (std::make_pair (id, npc.mId + " speed has zero value")); + messages.push_back (std::make_pair (id, "Speed is equal to zero")); if (npc.mNpdt.mWillpower == 0) - messages.push_back (std::make_pair (id, npc.mId + " willpower has zero value")); + messages.push_back (std::make_pair (id, "Willpower is equal to zero")); } - if (level < 1) - messages.push_back (std::make_pair (id, npc.mId + " level is non positive")); + if (level <= 0) + messages.push_back (std::make_pair (id, "Level is non-positive")); if (gold < 0) - messages.push_back (std::make_pair (id, npc.mId + " gold has negative value")); + messages.push_back (std::make_pair (id, "Gold count is negative")); if (npc.mName.empty()) - messages.push_back (std::make_pair (id, npc.mId + " has any empty name")); + messages.push_back (std::make_pair (id, "Name is missing")); if (npc.mClass.empty()) - messages.push_back (std::make_pair (id, npc.mId + " has an empty class")); + messages.push_back (std::make_pair (id, "Class is missing")); else if (mClasses.searchId (npc.mClass) == -1) - messages.push_back (std::make_pair (id, npc.mId + " has invalid class")); + messages.push_back (std::make_pair (id, "Class '" + npc.mClass + "' does not exist")); if (npc.mRace.empty()) - messages.push_back (std::make_pair (id, npc.mId + " has an empty race")); + messages.push_back (std::make_pair (id, "Race is missing")); else if (mRaces.searchId (npc.mRace) == -1) - messages.push_back (std::make_pair (id, npc.mId + " has invalid race")); + messages.push_back (std::make_pair (id, "Race '" + npc.mRace + "' does not exist")); if (disposition < 0) - messages.push_back (std::make_pair (id, npc.mId + " has negative disposition")); + messages.push_back (std::make_pair (id, "Disposition is negative")); - if (reputation < 0) //It seems that no character in Morrowind.esm have negative reputation. I'm assuming that negative reputation is invalid - { - messages.push_back (std::make_pair (id, npc.mId + " has negative reputation")); - } + if (reputation < 0) + messages.push_back (std::make_pair (id, "Reputation is negative")); if (!npc.mFaction.empty()) { if (rank < 0) - messages.push_back (std::make_pair (id, npc.mId + " has negative rank")); + messages.push_back (std::make_pair (id, "Faction rank is negative")); if (mFactions.searchId(npc.mFaction) == -1) - messages.push_back (std::make_pair (id, npc.mId + " has invalid faction")); + messages.push_back (std::make_pair (id, "Faction '" + npc.mFaction + "' does not exist")); } if (npc.mHead.empty()) - messages.push_back (std::make_pair (id, npc.mId + " has no head")); + messages.push_back (std::make_pair (id, "Head is missing")); // ADD CHECK HERE if (npc.mHair.empty()) - messages.push_back (std::make_pair (id, npc.mId + " has no hair")); + messages.push_back (std::make_pair (id, "Hair is missing")); // ADD CHECK HERE //TODO: reputation, Disposition, rank, everything else @@ -793,28 +789,25 @@ void CSMTools::ReferenceableCheckStage::weaponCheck( weapon.mData.mType == ESM::Weapon::Bolt)) { if (weapon.mData.mSlash[0] > weapon.mData.mSlash[1]) - messages.push_back (std::make_pair (id, - weapon.mId + " has minimum slash damage higher than maximum")); + messages.push_back (std::make_pair (id, "Minimum slash damage higher than maximum")); if (weapon.mData.mThrust[0] > weapon.mData.mThrust[1]) - messages.push_back (std::make_pair (id, - weapon.mId + " has minimum thrust damage higher than maximum")); + messages.push_back (std::make_pair (id, "Minimum thrust damage is higher than maximum")); } if (weapon.mData.mChop[0] > weapon.mData.mChop[1]) - messages.push_back (std::make_pair (id, - weapon.mId + " has minimum chop damage higher than maximum")); + messages.push_back (std::make_pair (id, "Minimum chop damage is higher than maximum")); if (!(weapon.mData.mType == ESM::Weapon::Arrow || weapon.mData.mType == ESM::Weapon::Bolt || weapon.mData.mType == ESM::Weapon::MarksmanThrown)) { //checking of health - if (weapon.mData.mHealth <= 0) - messages.push_back (std::make_pair (id, weapon.mId + " has non-positive health")); + if (weapon.mData.mHealth == 0) + messages.push_back (std::make_pair (id, "Durability is equal to zero")); if (weapon.mData.mReach < 0) - messages.push_back (std::make_pair (id, weapon.mId + " has negative reach")); + messages.push_back (std::make_pair (id, "Reach is negative")); } } @@ -877,7 +870,7 @@ void CSMTools::ReferenceableCheckStage::staticCheck ( CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Static, staticElement.mId); if (staticElement.mModel.empty()) - messages.push_back (std::make_pair (id, staticElement.mId + " has no model")); + messages.push_back (std::make_pair (id, "Model is missing")); } //final check @@ -886,7 +879,7 @@ void CSMTools::ReferenceableCheckStage::finalCheck (CSMDoc::Messages& messages) { if (!mPlayerPresent) messages.push_back (std::make_pair (CSMWorld::UniversalId::Type_Referenceables, - "There is no player record")); + "Player record is missing")); } void CSMTools::ReferenceableCheckStage::inventoryListCheck( @@ -900,8 +893,7 @@ void CSMTools::ReferenceableCheckStage::inventoryListCheck( CSMWorld::RefIdData::LocalIndex localIndex = mReferencables.searchId(itemName); if (localIndex.first == -1) - messages.push_back (std::make_pair (id, - id + " contains non-existing item (" + itemName + ")")); + messages.push_back (std::make_pair (id, "Item '" + itemName + "' does not exist")); else { // Needs to accommodate containers, creatures, and NPCs @@ -922,8 +914,7 @@ void CSMTools::ReferenceableCheckStage::inventoryListCheck( case CSMWorld::UniversalId::Type_ItemLevelledList: break; default: - messages.push_back (std::make_pair(id, - id + " contains item of invalid type (" + itemName + ")")); + messages.push_back (std::make_pair(id, "'" + itemName + "' is not an item")); } } } @@ -935,67 +926,66 @@ template void CSMTools::ReferenceableCheckStage::inventoryItemChe const Item& someItem, CSMDoc::Messages& messages, const std::string& someID, bool enchantable) { if (someItem.mName.empty()) - messages.push_back (std::make_pair (someID, someItem.mId + " has an empty name")); + messages.push_back (std::make_pair (someID, "Name is missing")); //Checking for weight if (someItem.mData.mWeight < 0) - messages.push_back (std::make_pair (someID, someItem.mId + " has negative weight")); + messages.push_back (std::make_pair (someID, "Weight is negative")); //Checking for value if (someItem.mData.mValue < 0) - messages.push_back (std::make_pair (someID, someItem.mId + " has negative value")); + messages.push_back (std::make_pair (someID, "Value is negative")); //checking for model if (someItem.mModel.empty()) - messages.push_back (std::make_pair (someID, someItem.mId + " has no model")); + messages.push_back (std::make_pair (someID, "Model is missing")); // ADD CHECK HERE //checking for icon if (someItem.mIcon.empty()) - messages.push_back (std::make_pair (someID, someItem.mId + " has no icon")); + messages.push_back (std::make_pair (someID, "Icon is missing")); // ADD CHECK HERE if (enchantable && someItem.mData.mEnchant < 0) - messages.push_back (std::make_pair (someID, someItem.mId + " has negative enchantment")); + messages.push_back (std::make_pair (someID, "Enchantment points number is negative")); } template void CSMTools::ReferenceableCheckStage::inventoryItemCheck ( const Item& someItem, CSMDoc::Messages& messages, const std::string& someID) { if (someItem.mName.empty()) - messages.push_back (std::make_pair (someID, someItem.mId + " has an empty name")); + messages.push_back (std::make_pair (someID, "Name is missing")); //Checking for weight if (someItem.mData.mWeight < 0) - messages.push_back (std::make_pair (someID, someItem.mId + " has negative weight")); + messages.push_back (std::make_pair (someID, "Weight is negative")); //Checking for value if (someItem.mData.mValue < 0) - messages.push_back (std::make_pair (someID, someItem.mId + " has negative value")); + messages.push_back (std::make_pair (someID, "Value is negative")); //checking for model if (someItem.mModel.empty()) - messages.push_back (std::make_pair (someID, someItem.mId + " has no model")); + messages.push_back (std::make_pair (someID, "Model is missing")); // ADD CHECK HERE //checking for icon if (someItem.mIcon.empty()) - messages.push_back (std::make_pair (someID, someItem.mId + " has no icon")); + messages.push_back (std::make_pair (someID, "Icon is missing")); // ADD CHECK HERE } template void CSMTools::ReferenceableCheckStage::toolCheck ( const Tool& someTool, CSMDoc::Messages& messages, const std::string& someID, bool canBeBroken) { if (someTool.mData.mQuality <= 0) - messages.push_back (std::make_pair (someID, someTool.mId + " has non-positive quality")); + messages.push_back (std::make_pair (someID, "Quality is non-positive")); if (canBeBroken && someTool.mData.mUses<=0) - messages.push_back (std::make_pair (someID, - someTool.mId + " has non-positive uses count")); + messages.push_back (std::make_pair (someID, "Number of uses is non-positive")); } template void CSMTools::ReferenceableCheckStage::toolCheck ( const Tool& someTool, CSMDoc::Messages& messages, const std::string& someID) { if (someTool.mData.mQuality <= 0) - messages.push_back (std::make_pair (someID, someTool.mId + " has non-positive quality")); + messages.push_back (std::make_pair (someID, "Quality is non-positive")); } template void CSMTools::ReferenceableCheckStage::listCheck ( @@ -1004,12 +994,10 @@ template void CSMTools::ReferenceableCheckStage::listCheck ( for (unsigned i = 0; i < someList.mList.size(); ++i) { if (mReferencables.searchId(someList.mList[i].mId).first == -1) - messages.push_back (std::make_pair (someID, - someList.mId + " contains item without referencable")); + messages.push_back (std::make_pair (someID, "Object '" + someList.mList[i].mId + "' does not exist")); if (someList.mList[i].mLevel < 1) - messages.push_back (std::make_pair (someID, - someList.mId + " contains item with non-positive level")); + messages.push_back (std::make_pair (someID, "Level of item '" + someList.mList[i].mId + "' is non-positive")); } } @@ -1019,6 +1007,6 @@ template void CSMTools::ReferenceableCheckStage::scriptCheck ( if (!someTool.mScript.empty()) { if (mScripts.searchId(someTool.mScript) == -1) - messages.push_back (std::make_pair (someID, someTool.mId + " refers to an unknown script \""+someTool.mScript+"\"")); + messages.push_back (std::make_pair (someID, "Script '"+someTool.mScript+"' does not exist")); } } diff --git a/apps/opencs/model/tools/scriptcheck.cpp b/apps/opencs/model/tools/scriptcheck.cpp index d3c6221cd..e62f9eb88 100644 --- a/apps/opencs/model/tools/scriptcheck.cpp +++ b/apps/opencs/model/tools/scriptcheck.cpp @@ -30,10 +30,7 @@ void CSMTools::ScriptCheckStage::report (const std::string& message, const Compi CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Script, mId); - stream - << "script " << mFile - << ", line " << loc.mLine << ", column " << loc.mColumn - << " (" << loc.mLiteral << "): " << message; + stream << "Line " << loc.mLine << ", column " << loc.mColumn << " (" << loc.mLiteral << "): " << message; std::ostringstream hintStream; @@ -47,7 +44,7 @@ void CSMTools::ScriptCheckStage::report (const std::string& message, Type type) CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Script, mId); std::ostringstream stream; - stream << "script " << mFile << ": " << message; + stream << message; mMessages->add (id, stream.str(), "", getSeverity (type)); } @@ -128,7 +125,7 @@ void CSMTools::ScriptCheckStage::perform (int stage, CSMDoc::Messages& messages) CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Script, mId); std::ostringstream stream; - stream << "script " << mFile << ": " << error.what(); + stream << error.what(); messages.add (id, stream.str(), "", CSMDoc::Message::Severity_SeriousError); } diff --git a/apps/opencs/model/tools/soundcheck.cpp b/apps/opencs/model/tools/soundcheck.cpp index b84453b5c..3fdbcc3bc 100644 --- a/apps/opencs/model/tools/soundcheck.cpp +++ b/apps/opencs/model/tools/soundcheck.cpp @@ -36,5 +36,5 @@ void CSMTools::SoundCheckStage::perform (int stage, CSMDoc::Messages& messages) if (sound.mData.mMinRange>sound.mData.mMaxRange) messages.push_back (std::make_pair (id, "Minimum range larger than maximum range")); - /// \todo check, if the sound file exists + /// \todo check, if the sound file exists ADD CHECK HERE } From 7535daa94da38e1259c4aeb61b21bb98e9c4e786 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Fri, 24 Aug 2018 21:15:43 +0300 Subject: [PATCH 013/163] Update class and race record verifier messages --- apps/opencs/model/tools/classcheck.cpp | 15 +++++++-------- apps/opencs/model/tools/racecheck.cpp | 12 ++++++------ 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/apps/opencs/model/tools/classcheck.cpp b/apps/opencs/model/tools/classcheck.cpp index 89923a398..157dfab7f 100644 --- a/apps/opencs/model/tools/classcheck.cpp +++ b/apps/opencs/model/tools/classcheck.cpp @@ -37,11 +37,11 @@ void CSMTools::ClassCheckStage::perform (int stage, CSMDoc::Messages& messages) // A class should have a name if (class_.mName.empty()) - messages.push_back (std::make_pair (id, class_.mId + " doesn't have a name")); + messages.push_back (std::make_pair (id, "Name is missing")); // A playable class should have a description if (class_.mData.mIsPlayable != 0 && class_.mDescription.empty()) - messages.push_back (std::make_pair (id, class_.mId + " doesn't have a description and it's playable")); + messages.push_back (std::make_pair (id, "Description of a playable class is missing")); // test for invalid attributes for (int i=0; i<2; ++i) @@ -49,14 +49,14 @@ void CSMTools::ClassCheckStage::perform (int stage, CSMDoc::Messages& messages) { std::ostringstream stream; - stream << "Attribute #" << i << " of " << class_.mId << " is not set"; + stream << "Attribute #" << i << " is not set"; messages.push_back (std::make_pair (id, stream.str())); } if (class_.mData.mAttribute[0]==class_.mData.mAttribute[1] && class_.mData.mAttribute[0]!=-1) { - messages.push_back (std::make_pair (id, "Class lists same attribute twice")); + messages.push_back (std::make_pair (id, "Same attribute is listed twice")); } // test for non-unique skill @@ -66,10 +66,9 @@ void CSMTools::ClassCheckStage::perform (int stage, CSMDoc::Messages& messages) for (int i2=0; i2<2; ++i2) ++skills[class_.mData.mSkills[i][i2]]; - for (std::map::const_iterator iter (skills.begin()); iter!=skills.end(); ++iter) - if (iter->second>1) + for (auto &skill : skills) + if (skill.second>1) { - messages.push_back (std::make_pair (id, - ESM::Skill::indexToId (iter->first) + " is listed more than once")); + messages.push_back (std::make_pair (id, "Skill " + ESM::Skill::indexToId (skill.first) + " is listed more than once")); } } diff --git a/apps/opencs/model/tools/racecheck.cpp b/apps/opencs/model/tools/racecheck.cpp index 38abfef18..7ce9a8869 100644 --- a/apps/opencs/model/tools/racecheck.cpp +++ b/apps/opencs/model/tools/racecheck.cpp @@ -29,24 +29,24 @@ void CSMTools::RaceCheckStage::performPerRecord (int stage, CSMDoc::Messages& me // test for empty name and description if (race.mName.empty()) - messages.push_back (std::make_pair (id, race.mId + " has an empty name")); + messages.push_back (std::make_pair (id, "Name is missing")); if (race.mDescription.empty()) - messages.push_back (std::make_pair (id, race.mId + " has an empty description")); + messages.push_back (std::make_pair (id, "Description is missing")); // test for positive height if (race.mData.mHeight.mMale<=0) - messages.push_back (std::make_pair (id, "male " + race.mId + " has non-positive height")); + messages.push_back (std::make_pair (id, "Male height is non-positive")); if (race.mData.mHeight.mFemale<=0) - messages.push_back (std::make_pair (id, "female " + race.mId + " has non-positive height")); + messages.push_back (std::make_pair (id, "Female height is non-positive")); // test for non-negative weight if (race.mData.mWeight.mMale<0) - messages.push_back (std::make_pair (id, "male " + race.mId + " has negative weight")); + messages.push_back (std::make_pair (id, "Male weight is negative")); if (race.mData.mWeight.mFemale<0) - messages.push_back (std::make_pair (id, "female " + race.mId + " has negative weight")); + messages.push_back (std::make_pair (id, "Female weight is negative")); /// \todo check data members that can't be edited in the table view } From a9ce155a7bdacabf351e4a4ec394046a4f0a09ee Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Fri, 24 Aug 2018 21:41:10 +0300 Subject: [PATCH 014/163] Update faction and body part record verifier messages --- apps/opencs/model/tools/bodypartcheck.cpp | 15 +++++++-------- apps/opencs/model/tools/factioncheck.cpp | 11 +++++------ 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/apps/opencs/model/tools/bodypartcheck.cpp b/apps/opencs/model/tools/bodypartcheck.cpp index b5bd78f6c..d33705cf6 100644 --- a/apps/opencs/model/tools/bodypartcheck.cpp +++ b/apps/opencs/model/tools/bodypartcheck.cpp @@ -34,25 +34,24 @@ void CSMTools::BodyPartCheckStage::perform (int stage, CSMDoc::Messages &message // Check BYDT if (bodyPart.mData.mPart > 14 ) - messages.push_back(std::make_pair( id, bodyPart.mId + " has out of range part value." )); + messages.push_back(std::make_pair(id, "Invalid mesh part")); if (bodyPart.mData.mFlags > 3 ) - messages.push_back(std::make_pair( id, bodyPart.mId + " has out of range flags value." )); + messages.push_back(std::make_pair(id, "Invalid flags")); if (bodyPart.mData.mType > 2 ) - messages.push_back(std::make_pair( id, bodyPart.mId + " has out of range type value." )); + messages.push_back(std::make_pair(id, "Invalid type")); // Check MODL if ( bodyPart.mModel.empty() ) - messages.push_back(std::make_pair( id, bodyPart.mId + " has no model." )); + messages.push_back(std::make_pair(id, "Model is missing" )); else if ( mMeshes.searchId( bodyPart.mModel ) == -1 ) - messages.push_back(std::make_pair( id, bodyPart.mId + " has invalid model." )); + messages.push_back(std::make_pair(id, "Model '" + bodyPart.mModel + "' does not exist")); // Check FNAM - if ( bodyPart.mRace.empty() ) - messages.push_back(std::make_pair( id, bodyPart.mId + " has no race." )); + messages.push_back(std::make_pair(id, "Race is missing" )); else if ( mRaces.searchId( bodyPart.mRace ) == -1 ) - messages.push_back(std::make_pair( id, bodyPart.mId + " has invalid race." )); + messages.push_back(std::make_pair(id, "Race '" + bodyPart.mRace + " does not exist")); } diff --git a/apps/opencs/model/tools/factioncheck.cpp b/apps/opencs/model/tools/factioncheck.cpp index 39073db5f..d19c0ae52 100644 --- a/apps/opencs/model/tools/factioncheck.cpp +++ b/apps/opencs/model/tools/factioncheck.cpp @@ -37,12 +37,12 @@ void CSMTools::FactionCheckStage::perform (int stage, CSMDoc::Messages& messages // test for empty name if (faction.mName.empty()) - messages.push_back (std::make_pair (id, faction.mId + " has an empty name")); + messages.push_back (std::make_pair (id, "Name is missing")); // test for invalid attributes if (faction.mData.mAttribute[0]==faction.mData.mAttribute[1] && faction.mData.mAttribute[0]!=-1) { - messages.push_back (std::make_pair (id , "Faction lists same attribute twice")); + messages.push_back (std::make_pair (id, "Same attribute is listed twice")); } // test for non-unique skill @@ -52,11 +52,10 @@ void CSMTools::FactionCheckStage::perform (int stage, CSMDoc::Messages& messages if (faction.mData.mSkills[i]!=-1) ++skills[faction.mData.mSkills[i]]; - for (std::map::const_iterator iter (skills.begin()); iter!=skills.end(); ++iter) - if (iter->second>1) + for (auto &skill : skills) + if (skill.second>1) { - messages.push_back (std::make_pair (id, - ESM::Skill::indexToId (iter->first) + " is listed more than once")); + messages.push_back (std::make_pair (id, "Skill " + ESM::Skill::indexToId (skill.first) + " is listed more than once")); } /// \todo check data members that can't be edited in the table view From 14ef145b3b83691e00c2d2c433ab76e069b5e4a2 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Fri, 24 Aug 2018 22:01:08 +0300 Subject: [PATCH 015/163] Update region and pathgrid record verifier messages --- apps/opencs/model/tools/pathgridcheck.cpp | 37 +++++++++++------------ apps/opencs/model/tools/regioncheck.cpp | 2 +- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/apps/opencs/model/tools/pathgridcheck.cpp b/apps/opencs/model/tools/pathgridcheck.cpp index c25845885..e63e9122f 100644 --- a/apps/opencs/model/tools/pathgridcheck.cpp +++ b/apps/opencs/model/tools/pathgridcheck.cpp @@ -37,9 +37,9 @@ void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& message // check the number of pathgrid points if (pathgrid.mData.mS2 < static_cast(pathgrid.mPoints.size())) - messages.add (id, pathgrid.mId + " has less points than expected", "", CSMDoc::Message::Severity_Error); + messages.add (id, "Less points than expected", "", CSMDoc::Message::Severity_Error); else if (pathgrid.mData.mS2 > static_cast(pathgrid.mPoints.size())) - messages.add (id, pathgrid.mId + " has more points than expected", "", CSMDoc::Message::Severity_Error); + messages.add (id, "More points than expected", "", CSMDoc::Message::Severity_Error); std::vector pointList(pathgrid.mPoints.size()); std::vector duplList; @@ -56,9 +56,9 @@ void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& message if (pointList[pathgrid.mEdges[i].mV0].mOtherIndex[j] == pathgrid.mEdges[i].mV1) { std::ostringstream ss; - ss << "has a duplicate edge between points" << pathgrid.mEdges[i].mV0 - << " and " << pathgrid.mEdges[i].mV1; - messages.add (id, pathgrid.mId + ss.str(), "", CSMDoc::Message::Severity_Error); + ss << "Duplicate edge between points" + << pathgrid.mEdges[i].mV0 << " and " << pathgrid.mEdges[i].mV1; + messages.add (id, ss.str(), "", CSMDoc::Message::Severity_Error); break; } } @@ -70,8 +70,8 @@ void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& message else { std::ostringstream ss; - ss << " has an edge connecting a non-existent point " << pathgrid.mEdges[i].mV0; - messages.add (id, pathgrid.mId + ss.str(), "", CSMDoc::Message::Severity_Error); + ss << "An edge is connected to a non-existent point " << pathgrid.mEdges[i].mV0; + messages.add (id, ss.str(), "", CSMDoc::Message::Severity_Error); } } @@ -93,8 +93,8 @@ void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& message if (!foundReverse) { std::ostringstream ss; - ss << " has a missing edge between points " << i << " and " << pointList[i].mOtherIndex[j]; - messages.add (id, pathgrid.mId + ss.str(), "", CSMDoc::Message::Severity_Error); + ss << "Missing edge between points " << i << " and " << pointList[i].mOtherIndex[j]; + messages.add (id, ss.str(), "", CSMDoc::Message::Severity_Error); } } @@ -113,11 +113,9 @@ void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& message if (it == duplList.end()) { std::ostringstream ss; - ss << " has a duplicated point (" << i - << ") x=" << pathgrid.mPoints[i].mX - << ", y=" << pathgrid.mPoints[i].mY - << ", z=" << pathgrid.mPoints[i].mZ; - messages.add (id, pathgrid.mId + ss.str(), "", CSMDoc::Message::Severity_Warning); + ss << "Point " << i << " duplicates point " << j + << " (" << pathgrid.mPoints[i].mX << ", " << pathgrid.mPoints[i].mY << ", " << pathgrid.mPoints[i].mZ << ")"; + messages.add (id, ss.str(), "", CSMDoc::Message::Severity_Warning); duplList.push_back(i); break; @@ -132,11 +130,12 @@ void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& message if (pointList[i].mConnectionNum == 0) { std::ostringstream ss; - ss << " has an orphaned point (" << i - << ") x=" << pathgrid.mPoints[i].mX - << ", y=" << pathgrid.mPoints[i].mY - << ", z=" << pathgrid.mPoints[i].mZ; - messages.add (id, pathgrid.mId + ss.str(), "", CSMDoc::Message::Severity_Warning); + ss << "Point " << i << " (" + << pathgrid.mPoints[i].mX << ", " + << pathgrid.mPoints[i].mY << ", " + << pathgrid.mPoints[i].mZ << ") " + << "is disconnected from other points"; + messages.add (id, ss.str(), "", CSMDoc::Message::Severity_Warning); } } diff --git a/apps/opencs/model/tools/regioncheck.cpp b/apps/opencs/model/tools/regioncheck.cpp index f21253090..8375e3f0a 100644 --- a/apps/opencs/model/tools/regioncheck.cpp +++ b/apps/opencs/model/tools/regioncheck.cpp @@ -36,7 +36,7 @@ void CSMTools::RegionCheckStage::perform (int stage, CSMDoc::Messages& messages) // test for empty name if (region.mName.empty()) - messages.add(id, region.mId + " has an empty name", "", CSMDoc::Message::Severity_Error); + messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Error); /// \todo test that the ID in mSleeplist exists From a6d3cd919016c060bc1d2735e85f5653d0db53d1 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Fri, 24 Aug 2018 23:12:11 +0300 Subject: [PATCH 016/163] Update topic info verifier messages --- apps/opencs/model/tools/magiceffectcheck.cpp | 4 +- apps/opencs/model/tools/topicinfocheck.cpp | 109 ++++++++----------- apps/opencs/model/tools/topicinfocheck.hpp | 11 -- 3 files changed, 48 insertions(+), 76 deletions(-) diff --git a/apps/opencs/model/tools/magiceffectcheck.cpp b/apps/opencs/model/tools/magiceffectcheck.cpp index 448e4d28c..7e7a09aa5 100644 --- a/apps/opencs/model/tools/magiceffectcheck.cpp +++ b/apps/opencs/model/tools/magiceffectcheck.cpp @@ -20,7 +20,7 @@ namespace std::string CSMTools::MagicEffectCheckStage::checkTexture(const std::string &texture, bool isIcon) const { - if (texture.empty()) return (isIcon ? "Icon is not specified" : std::string()); + if (texture.empty()) return (isIcon ? "Icon is missing" : std::string()); const CSMWorld::Resources &textures = isIcon ? mIcons : mTextures; if (textures.searchId(texture) != -1) return std::string(); @@ -97,7 +97,7 @@ void CSMTools::MagicEffectCheckStage::perform(int stage, CSMDoc::Messages &messa { addMessage(messages, id, "Description is missing"); } - + if (effect.mData.mBaseCost < 0.0f) { addMessage(messages, id, "Base cost is negative"); diff --git a/apps/opencs/model/tools/topicinfocheck.cpp b/apps/opencs/model/tools/topicinfocheck.cpp index ac1f596ae..9a31db72b 100644 --- a/apps/opencs/model/tools/topicinfocheck.cpp +++ b/apps/opencs/model/tools/topicinfocheck.cpp @@ -134,7 +134,7 @@ void CSMTools::TopicInfoCheckStage::perform(int stage, CSMDoc::Messages& message if (topicInfo.mData.mGender < -1 || topicInfo.mData.mGender > 1) { std::ostringstream stream; - messages.add(id, "Gender: Value is invalid", "", CSMDoc::Message::Severity_Error); + messages.add(id, "Gender is invalid", "", CSMDoc::Message::Severity_Error); } if (!topicInfo.mRace.empty()) @@ -166,23 +166,28 @@ void CSMTools::TopicInfoCheckStage::perform(int stage, CSMDoc::Messages& message bool CSMTools::TopicInfoCheckStage::verifyActor(const std::string& actor, const CSMWorld::UniversalId& id, CSMDoc::Messages& messages) { - const std::string specifier = "Actor"; - CSMWorld::RefIdData::LocalIndex index = mReferencables.searchId(actor); if (index.first == -1) { - writeMissingIdError(specifier, actor, id, messages); + std::ostringstream stream; + stream << "Actor '" << actor << "' does not exist"; + messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); return false; } else if (mReferencables.getRecord(index).isDeleted()) { - writeDeletedRecordError(specifier, actor, id, messages); + std::ostringstream stream; + stream << "Deleted actor '" << actor << "' is being referenced"; + messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); return false; } else if (index.second != CSMWorld::UniversalId::Type_Npc && index.second != CSMWorld::UniversalId::Type_Creature) { - writeInvalidTypeError(specifier, actor, index.second, "NPC or Creature", id, messages); + CSMWorld::UniversalId tempId(index.second, actor); + std::ostringstream stream; + stream << "Object '" << actor << "' has invalid type " << tempId.getTypeName() << " (an actor must be an NPC or a creature)"; + messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); return false; } @@ -192,11 +197,11 @@ bool CSMTools::TopicInfoCheckStage::verifyActor(const std::string& actor, const bool CSMTools::TopicInfoCheckStage::verifyCell(const std::string& cell, const CSMWorld::UniversalId& id, CSMDoc::Messages& messages) { - const std::string specifier = "Cell"; - if (mCellNames.find(cell) == mCellNames.end()) { - writeMissingIdError(specifier, cell, id, messages); + std::ostringstream stream; + stream << "Cell '" << cell << "' does not exist"; + messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); return false; } @@ -209,9 +214,8 @@ bool CSMTools::TopicInfoCheckStage::verifyFactionRank(const std::string& faction if (rank < -1) { std::ostringstream stream; - stream << "Rank or PC Rank is set to " << rank << ", but should be set to -1 if no rank is required"; - - messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); + stream << "Faction rank is set to " << rank << ", but it should be set to -1 if there are no rank requirements"; + messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Warning); return false; } @@ -229,10 +233,10 @@ bool CSMTools::TopicInfoCheckStage::verifyFactionRank(const std::string& faction if (rank >= limit) { std::ostringstream stream; - stream << "Rank or PC Rank is set to " << rank << " which is more than the maximum of " << limit - 1 - << " for the " << factionName << " faction"; + stream << "Faction rank is set to " << rank << " which is more than the maximum of " << limit - 1 + << " for the '" << factionName << "' faction"; - messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); + messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Warning); return false; } @@ -242,18 +246,20 @@ bool CSMTools::TopicInfoCheckStage::verifyFactionRank(const std::string& faction bool CSMTools::TopicInfoCheckStage::verifyItem(const std::string& item, const CSMWorld::UniversalId& id, CSMDoc::Messages& messages) { - const std::string specifier = "Item"; - CSMWorld::RefIdData::LocalIndex index = mReferencables.searchId(item); if (index.first == -1) { - writeMissingIdError(specifier, item, id, messages); + std::ostringstream stream; + stream << "Item '" << item << "' does not exist"; + messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); return false; } else if (mReferencables.getRecord(index).isDeleted()) { - writeDeletedRecordError(specifier, item, id, messages); + std::ostringstream stream; + stream << "Deleted item '" << item << "' is being referenced"; + messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); return false; } else @@ -276,8 +282,13 @@ bool CSMTools::TopicInfoCheckStage::verifyItem(const std::string& item, const CS break; default: - writeInvalidTypeError(specifier, item, index.second, "Potion, Armor, Book, etc.", id, messages); + { + CSMWorld::UniversalId tempId(index.second, item); + std::ostringstream stream; + stream << "Object '" << item << "' has invalid type " << tempId.getTypeName() << " (an item can be a potion, an armor piece, a book and so on)"; + messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); return false; + } } } @@ -291,13 +302,13 @@ bool CSMTools::TopicInfoCheckStage::verifySelectStruct(const ESM::DialInfo::Sele if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_None) { - messages.add(id, "Invalid Info Condition: " + infoCondition.toString(), "", CSMDoc::Message::Severity_Error); + messages.add(id, "Invalid condition '" + infoCondition.toString() + "'", "", CSMDoc::Message::Severity_Error); return false; } else if (!infoCondition.variantTypeIsValid()) { std::ostringstream stream; - stream << "Info Condition: Value for \"" << infoCondition.toString() << "\" has a type of "; + stream << "Value of condition '" << infoCondition.toString() << "' has invalid "; switch (select.mValue.getType()) { @@ -307,8 +318,9 @@ bool CSMTools::TopicInfoCheckStage::verifySelectStruct(const ESM::DialInfo::Sele case ESM::VT_Long: stream << "Long"; break; case ESM::VT_Float: stream << "Float"; break; case ESM::VT_String: stream << "String"; break; - default: stream << "Unknown"; break; + default: stream << "unknown"; break; } + stream << " type"; messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); return false; @@ -316,7 +328,7 @@ bool CSMTools::TopicInfoCheckStage::verifySelectStruct(const ESM::DialInfo::Sele else if (infoCondition.conditionIsAlwaysTrue()) { std::ostringstream stream; - stream << "Info Condition: " << infoCondition.toString() << " is always true"; + stream << "Condition '" << infoCondition.toString() << "' is always true"; messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Warning); return false; @@ -324,7 +336,7 @@ bool CSMTools::TopicInfoCheckStage::verifySelectStruct(const ESM::DialInfo::Sele else if (infoCondition.conditionIsNeverTrue()) { std::ostringstream stream; - stream << "Info Condition: " << infoCondition.toString() << " is never true"; + stream << "Condition '" << infoCondition.toString() << "' is never true"; messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Warning); return false; @@ -383,11 +395,11 @@ bool CSMTools::TopicInfoCheckStage::verifySelectStruct(const ESM::DialInfo::Sele bool CSMTools::TopicInfoCheckStage::verifySound(const std::string& sound, const CSMWorld::UniversalId& id, CSMDoc::Messages& messages) { - const std::string specifier = "Sound File"; - if (mSoundFiles.searchId(sound) == -1) { - writeMissingIdError(specifier, sound, id, messages); + std::ostringstream stream; + stream << "Sound file '" << sound << "' does not exist"; + messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); return false; } @@ -402,47 +414,18 @@ bool CSMTools::TopicInfoCheckStage::verifyId(const std::string& name, const CSMW if (index == -1) { - writeMissingIdError(T::getRecordType(), name, id, messages); + std::ostringstream stream; + stream << T::getRecordType() + " '" << name << "' does not exist"; + messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); return false; } else if (collection.getRecord(index).isDeleted()) { - writeDeletedRecordError(T::getRecordType(), name, id, messages); + std::ostringstream stream; + stream << "Deleted " << T::getRecordType() << " record '" << name << "' is being referenced"; + messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); return false; } return true; } - -// Error functions - -void CSMTools::TopicInfoCheckStage::writeMissingIdError(const std::string& specifier, const std::string& missingId, - const CSMWorld::UniversalId& id, CSMDoc::Messages& messages) -{ - std::ostringstream stream; - stream << specifier << ": ID or name \"" << missingId << "\" could not be found"; - - messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); -} - -void CSMTools::TopicInfoCheckStage::writeDeletedRecordError(const std::string& specifier, const std::string& recordId, - const CSMWorld::UniversalId& id, CSMDoc::Messages& messages) -{ - std::ostringstream stream; - stream << specifier << ": Deleted record with ID \"" << recordId << "\" is being referenced"; - - messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); -} - -void CSMTools::TopicInfoCheckStage::writeInvalidTypeError(const std::string& specifier, const std::string& invalidId, - CSMWorld::UniversalId::Type invalidType, const std::string& expectedType, const CSMWorld::UniversalId& id, - CSMDoc::Messages& messages) -{ - CSMWorld::UniversalId tempId(invalidType, invalidId); - - std::ostringstream stream; - stream << specifier << ": invalid type of " << tempId.getTypeName() << " was found for referencable \"" - << invalidId << "\" (can be of type " << expectedType << ")"; - - messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); -} diff --git a/apps/opencs/model/tools/topicinfocheck.hpp b/apps/opencs/model/tools/topicinfocheck.hpp index dbd5fe1c5..9575181b0 100644 --- a/apps/opencs/model/tools/topicinfocheck.hpp +++ b/apps/opencs/model/tools/topicinfocheck.hpp @@ -80,17 +80,6 @@ namespace CSMTools template bool verifyId(const std::string& name, const CSMWorld::IdCollection& collection, const CSMWorld::UniversalId& id, CSMDoc::Messages& messages); - - // Common error messages - void writeMissingIdError(const std::string& specifier, const std::string& missingId, - const CSMWorld::UniversalId& id, CSMDoc::Messages& messages); - - void writeDeletedRecordError(const std::string& specifier, const std::string& recordId, - const CSMWorld::UniversalId& id, CSMDoc::Messages& messages); - - void writeInvalidTypeError(const std::string& specifier, const std::string& invalidId, - CSMWorld::UniversalId::Type invalidType, const std::string& expectedType, - const CSMWorld::UniversalId& id, CSMDoc::Messages& messages); }; } From 1f717def35c32dfa78c356239dd0a32b83554e19 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 25 Aug 2018 00:27:42 +0300 Subject: [PATCH 017/163] Update journal, start script and spell verifiers messages --- apps/opencs/model/tools/journalcheck.cpp | 18 +++++------------- apps/opencs/model/tools/spellcheck.cpp | 4 ++-- apps/opencs/model/tools/startscriptcheck.cpp | 3 +-- 3 files changed, 8 insertions(+), 17 deletions(-) diff --git a/apps/opencs/model/tools/journalcheck.cpp b/apps/opencs/model/tools/journalcheck.cpp index 4a7ab7d66..bd77e95b6 100644 --- a/apps/opencs/model/tools/journalcheck.cpp +++ b/apps/opencs/model/tools/journalcheck.cpp @@ -33,6 +33,7 @@ void CSMTools::JournalCheckStage::perform(int stage, CSMDoc::Messages& messages) std::set questIndices; CSMWorld::InfoCollection::Range range = mJournalInfos.getTopicRange(journal.mId); + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Journal, journal.mId); for (CSMWorld::InfoCollection::RecordConstIterator it = range.first; it != range.second; ++it) { @@ -56,9 +57,7 @@ void CSMTools::JournalCheckStage::perform(int stage, CSMDoc::Messages& messages) if (journalInfo.mResponse.empty()) { - CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_JournalInfo, journalInfo.mId); - - messages.add(id, "Journal Info: missing description", "", CSMDoc::Message::Severity_Warning); + messages.add(id, "Missing journal entry text", "", CSMDoc::Message::Severity_Warning); } std::pair::iterator, bool> result = questIndices.insert(journalInfo.mData.mJournalIndex); @@ -66,25 +65,18 @@ void CSMTools::JournalCheckStage::perform(int stage, CSMDoc::Messages& messages) // Duplicate index if (result.second == false) { - CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_JournalInfo, journalInfo.mId); - std::ostringstream stream; - stream << "Journal: duplicated quest index " << journalInfo.mData.mJournalIndex; - + stream << "Duplicated quest index " << journalInfo.mData.mJournalIndex; messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); } } if (totalInfoCount == 0) { - CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Journal, journal.mId); - - messages.add(id, "Journal: no defined Journal Infos", "", CSMDoc::Message::Severity_Warning); + messages.add(id, "No related journal entry", "", CSMDoc::Message::Severity_Warning); } else if (statusNamedCount > 1) { - CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Journal, journal.mId); - - messages.add(id, "Journal: multiple infos with quest status \"Named\"", "", CSMDoc::Message::Severity_Error); + messages.add(id, "Multiple entries with quest status 'Named'", "", CSMDoc::Message::Severity_Error); } } diff --git a/apps/opencs/model/tools/spellcheck.cpp b/apps/opencs/model/tools/spellcheck.cpp index 3e59f0d9a..6c08aca37 100644 --- a/apps/opencs/model/tools/spellcheck.cpp +++ b/apps/opencs/model/tools/spellcheck.cpp @@ -36,11 +36,11 @@ void CSMTools::SpellCheckStage::perform (int stage, CSMDoc::Messages& messages) // test for empty name and description if (spell.mName.empty()) - messages.push_back (std::make_pair (id, spell.mId + " has an empty name")); + messages.push_back (std::make_pair (id, "Name is missing")); // test for invalid cost values if (spell.mData.mCost<0) - messages.push_back (std::make_pair (id, spell.mId + " has a negative spell costs")); + messages.push_back (std::make_pair (id, "Spell cost is negative")); /// \todo check data members that can't be edited in the table view } diff --git a/apps/opencs/model/tools/startscriptcheck.cpp b/apps/opencs/model/tools/startscriptcheck.cpp index b1d92380b..5a9f63dc4 100644 --- a/apps/opencs/model/tools/startscriptcheck.cpp +++ b/apps/opencs/model/tools/startscriptcheck.cpp @@ -25,8 +25,7 @@ void CSMTools::StartScriptCheckStage::perform(int stage, CSMDoc::Messages& messa CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_StartScript, scriptId); if (mScripts.searchId (Misc::StringUtils::lowerCase (scriptId))==-1) - messages.push_back ( - std::make_pair (id, "Start script " + scriptId + " does not exist")); + messages.push_back (std::make_pair (id, "Start script " + scriptId + " does not exist")); } int CSMTools::StartScriptCheckStage::setup() From 38ea7928f54876755d30a732fd36ed2db547d039 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 25 Aug 2018 00:47:38 +0300 Subject: [PATCH 018/163] Add sound file checks to sound record verifier --- apps/opencs/model/tools/soundcheck.cpp | 17 ++++++++++++++--- apps/opencs/model/tools/soundcheck.hpp | 9 ++++++++- apps/opencs/model/tools/tools.cpp | 2 +- 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/apps/opencs/model/tools/soundcheck.cpp b/apps/opencs/model/tools/soundcheck.cpp index 3fdbcc3bc..5708a59d3 100644 --- a/apps/opencs/model/tools/soundcheck.cpp +++ b/apps/opencs/model/tools/soundcheck.cpp @@ -6,10 +6,14 @@ #include "../prefs/state.hpp" +#include "../world/data.hpp" +#include "../world/resources.hpp" #include "../world/universalid.hpp" -CSMTools::SoundCheckStage::SoundCheckStage (const CSMWorld::IdCollection& sounds) -: mSounds (sounds) +CSMTools::SoundCheckStage::SoundCheckStage (const CSMWorld::IdCollection &sounds, + const CSMWorld::Resources &soundfiles) + : mSounds (sounds), + mSoundFiles (soundfiles) { mIgnoreBaseRecords = false; } @@ -36,5 +40,12 @@ void CSMTools::SoundCheckStage::perform (int stage, CSMDoc::Messages& messages) if (sound.mData.mMinRange>sound.mData.mMaxRange) messages.push_back (std::make_pair (id, "Minimum range larger than maximum range")); - /// \todo check, if the sound file exists ADD CHECK HERE + if (sound.mSound.empty()) + { + messages.push_back(std::make_pair(id, "Sound file is missing")); + } + else if (mSoundFiles.searchId(sound.mSound) == -1) + { + messages.push_back(std::make_pair(id, "Sound file '" + sound.mSound + "' does not exist")); + } } diff --git a/apps/opencs/model/tools/soundcheck.hpp b/apps/opencs/model/tools/soundcheck.hpp index d6fff5263..47c3249ce 100644 --- a/apps/opencs/model/tools/soundcheck.hpp +++ b/apps/opencs/model/tools/soundcheck.hpp @@ -7,17 +7,24 @@ #include "../doc/stage.hpp" +namespace CSMWorld +{ + class Resources; +} + namespace CSMTools { /// \brief VerifyStage: make sure that sound records are internally consistent class SoundCheckStage : public CSMDoc::Stage { const CSMWorld::IdCollection& mSounds; + const CSMWorld::Resources &mSoundFiles; bool mIgnoreBaseRecords; public: - SoundCheckStage (const CSMWorld::IdCollection& sounds); + SoundCheckStage (const CSMWorld::IdCollection& sounds, + const CSMWorld::Resources &soundfiles); virtual int setup(); ///< \return number of steps diff --git a/apps/opencs/model/tools/tools.cpp b/apps/opencs/model/tools/tools.cpp index 0946225cf..dfa2ae202 100644 --- a/apps/opencs/model/tools/tools.cpp +++ b/apps/opencs/model/tools/tools.cpp @@ -74,7 +74,7 @@ CSMDoc::OperationHolder *CSMTools::Tools::getVerifier() mVerifierOperation->appendStage (new RaceCheckStage (mData.getRaces())); - mVerifierOperation->appendStage (new SoundCheckStage (mData.getSounds())); + mVerifierOperation->appendStage (new SoundCheckStage (mData.getSounds(), mData.getResources (CSMWorld::UniversalId::Type_SoundsRes))); mVerifierOperation->appendStage (new RegionCheckStage (mData.getRegions())); From d90940011f7c1bd5f25dedc9cae476b61b1f7b15 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 25 Aug 2018 01:22:41 +0300 Subject: [PATCH 019/163] Add model checks to object record verifier --- .../opencs/model/tools/referenceablecheck.cpp | 123 ++++++++++-------- .../opencs/model/tools/referenceablecheck.hpp | 7 +- apps/opencs/model/tools/tools.cpp | 3 +- 3 files changed, 75 insertions(+), 58 deletions(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index 6d06cce12..8a1103c3d 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -11,13 +11,14 @@ CSMTools::ReferenceableCheckStage::ReferenceableCheckStage( const CSMWorld::RefIdData& referenceable, const CSMWorld::IdCollection& races, const CSMWorld::IdCollection& classes, const CSMWorld::IdCollection& faction, - const CSMWorld::IdCollection& scripts) - : - mReferencables(referenceable), + const CSMWorld::IdCollection& scripts, + const CSMWorld::Resources& models) + :mObjects(referenceable), mRaces(races), mClasses(classes), mFactions(faction), mScripts(scripts), + mModels(models), mPlayerPresent(false) { mIgnoreBaseRecords = false; @@ -26,201 +27,201 @@ CSMTools::ReferenceableCheckStage::ReferenceableCheckStage( void CSMTools::ReferenceableCheckStage::perform (int stage, CSMDoc::Messages& messages) { //Checks for books, than, when stage is above mBooksSize goes to other checks, with (stage - PrevSum) as stage. - const int bookSize(mReferencables.getBooks().getSize()); + const int bookSize(mObjects.getBooks().getSize()); if (stage < bookSize) { - bookCheck(stage, mReferencables.getBooks(), messages); + bookCheck(stage, mObjects.getBooks(), messages); return; } stage -= bookSize; - const int activatorSize(mReferencables.getActivators().getSize()); + const int activatorSize(mObjects.getActivators().getSize()); if (stage < activatorSize) { - activatorCheck(stage, mReferencables.getActivators(), messages); + activatorCheck(stage, mObjects.getActivators(), messages); return; } stage -= activatorSize; - const int potionSize(mReferencables.getPotions().getSize()); + const int potionSize(mObjects.getPotions().getSize()); if (stage < potionSize) { - potionCheck(stage, mReferencables.getPotions(), messages); + potionCheck(stage, mObjects.getPotions(), messages); return; } stage -= potionSize; - const int apparatusSize(mReferencables.getApparati().getSize()); + const int apparatusSize(mObjects.getApparati().getSize()); if (stage < apparatusSize) { - apparatusCheck(stage, mReferencables.getApparati(), messages); + apparatusCheck(stage, mObjects.getApparati(), messages); return; } stage -= apparatusSize; - const int armorSize(mReferencables.getArmors().getSize()); + const int armorSize(mObjects.getArmors().getSize()); if (stage < armorSize) { - armorCheck(stage, mReferencables.getArmors(), messages); + armorCheck(stage, mObjects.getArmors(), messages); return; } stage -= armorSize; - const int clothingSize(mReferencables.getClothing().getSize()); + const int clothingSize(mObjects.getClothing().getSize()); if (stage < clothingSize) { - clothingCheck(stage, mReferencables.getClothing(), messages); + clothingCheck(stage, mObjects.getClothing(), messages); return; } stage -= clothingSize; - const int containerSize(mReferencables.getContainers().getSize()); + const int containerSize(mObjects.getContainers().getSize()); if (stage < containerSize) { - containerCheck(stage, mReferencables.getContainers(), messages); + containerCheck(stage, mObjects.getContainers(), messages); return; } stage -= containerSize; - const int doorSize(mReferencables.getDoors().getSize()); + const int doorSize(mObjects.getDoors().getSize()); if (stage < doorSize) { - doorCheck(stage, mReferencables.getDoors(), messages); + doorCheck(stage, mObjects.getDoors(), messages); return; } stage -= doorSize; - const int ingredientSize(mReferencables.getIngredients().getSize()); + const int ingredientSize(mObjects.getIngredients().getSize()); if (stage < ingredientSize) { - ingredientCheck(stage, mReferencables.getIngredients(), messages); + ingredientCheck(stage, mObjects.getIngredients(), messages); return; } stage -= ingredientSize; - const int creatureLevListSize(mReferencables.getCreatureLevelledLists().getSize()); + const int creatureLevListSize(mObjects.getCreatureLevelledLists().getSize()); if (stage < creatureLevListSize) { - creaturesLevListCheck(stage, mReferencables.getCreatureLevelledLists(), messages); + creaturesLevListCheck(stage, mObjects.getCreatureLevelledLists(), messages); return; } stage -= creatureLevListSize; - const int itemLevelledListSize(mReferencables.getItemLevelledList().getSize()); + const int itemLevelledListSize(mObjects.getItemLevelledList().getSize()); if (stage < itemLevelledListSize) { - itemLevelledListCheck(stage, mReferencables.getItemLevelledList(), messages); + itemLevelledListCheck(stage, mObjects.getItemLevelledList(), messages); return; } stage -= itemLevelledListSize; - const int lightSize(mReferencables.getLights().getSize()); + const int lightSize(mObjects.getLights().getSize()); if (stage < lightSize) { - lightCheck(stage, mReferencables.getLights(), messages); + lightCheck(stage, mObjects.getLights(), messages); return; } stage -= lightSize; - const int lockpickSize(mReferencables.getLocpicks().getSize()); + const int lockpickSize(mObjects.getLocpicks().getSize()); if (stage < lockpickSize) { - lockpickCheck(stage, mReferencables.getLocpicks(), messages); + lockpickCheck(stage, mObjects.getLocpicks(), messages); return; } stage -= lockpickSize; - const int miscSize(mReferencables.getMiscellaneous().getSize()); + const int miscSize(mObjects.getMiscellaneous().getSize()); if (stage < miscSize) { - miscCheck(stage, mReferencables.getMiscellaneous(), messages); + miscCheck(stage, mObjects.getMiscellaneous(), messages); return; } stage -= miscSize; - const int npcSize(mReferencables.getNPCs().getSize()); + const int npcSize(mObjects.getNPCs().getSize()); if (stage < npcSize) { - npcCheck(stage, mReferencables.getNPCs(), messages); + npcCheck(stage, mObjects.getNPCs(), messages); return; } stage -= npcSize; - const int weaponSize(mReferencables.getWeapons().getSize()); + const int weaponSize(mObjects.getWeapons().getSize()); if (stage < weaponSize) { - weaponCheck(stage, mReferencables.getWeapons(), messages); + weaponCheck(stage, mObjects.getWeapons(), messages); return; } stage -= weaponSize; - const int probeSize(mReferencables.getProbes().getSize()); + const int probeSize(mObjects.getProbes().getSize()); if (stage < probeSize) { - probeCheck(stage, mReferencables.getProbes(), messages); + probeCheck(stage, mObjects.getProbes(), messages); return; } stage -= probeSize; - const int repairSize(mReferencables.getRepairs().getSize()); + const int repairSize(mObjects.getRepairs().getSize()); if (stage < repairSize) { - repairCheck(stage, mReferencables.getRepairs(), messages); + repairCheck(stage, mObjects.getRepairs(), messages); return; } stage -= repairSize; - const int staticSize(mReferencables.getStatics().getSize()); + const int staticSize(mObjects.getStatics().getSize()); if (stage < staticSize) { - staticCheck(stage, mReferencables.getStatics(), messages); + staticCheck(stage, mObjects.getStatics(), messages); return; } stage -= staticSize; - const int creatureSize(mReferencables.getCreatures().getSize()); + const int creatureSize(mObjects.getCreatures().getSize()); if (stage < creatureSize) { - creatureCheck(stage, mReferencables.getCreatures(), messages); + creatureCheck(stage, mObjects.getCreatures(), messages); return; } // if we come that far, we are about to perform our last, final check. @@ -233,7 +234,7 @@ int CSMTools::ReferenceableCheckStage::setup() mPlayerPresent = false; mIgnoreBaseRecords = CSMPrefs::get()["Reports"]["ignore-base-records"].isTrue(); - return mReferencables.getSize() + 1; + return mObjects.getSize() + 1; } void CSMTools::ReferenceableCheckStage::bookCheck( @@ -272,7 +273,9 @@ void CSMTools::ReferenceableCheckStage::activatorCheck( //Checking for model, IIRC all activators should have a model if (activator.mModel.empty()) - messages.push_back (std::make_pair (id, "Model is missing")); // ADD CHECK HERE + messages.push_back (std::make_pair (id, "Model is missing")); + else if (mModels.searchId(activator.mModel) == -1) + messages.push_back (std::make_pair (id, "Model '" + activator.mModel + "' does not exist")); // Check that mentioned scripts exist scriptCheck(activator, messages, id.toString()); @@ -383,9 +386,11 @@ void CSMTools::ReferenceableCheckStage::containerCheck( const ESM::Container& container = (dynamic_cast& >(baseRecord)).get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Container, container.mId); - //Checking for model, IIRC all containers should have a model + //Checking for model if (container.mModel.empty()) - messages.push_back (std::make_pair (id, "Model is missing")); // ADD CHECK HERE + messages.push_back (std::make_pair (id, "Model is missing")); + else if (mModels.searchId(container.mModel) == -1) + messages.push_back (std::make_pair (id, "Model '" + container.mModel + "' does not exist")); //Checking for capacity (weight) if (container.mWeight < 0) //0 is allowed @@ -416,7 +421,9 @@ void CSMTools::ReferenceableCheckStage::creatureCheck ( CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Creature, creature.mId); if (creature.mModel.empty()) - messages.push_back (std::make_pair (id, "Model is missing")); // ADD CHECK HERE + messages.push_back (std::make_pair (id, "Model is missing")); + else if (mModels.searchId(creature.mModel) == -1) + messages.push_back (std::make_pair (id, "Model '" + creature.mModel + "' does not exist")); if (creature.mName.empty()) messages.push_back (std::make_pair (id, "Name is missing")); @@ -496,7 +503,9 @@ void CSMTools::ReferenceableCheckStage::doorCheck( messages.push_back (std::make_pair (id, "Name is missing")); if (door.mModel.empty()) - messages.push_back (std::make_pair (id, "Model is missing")); // ADD CHECK HERE + messages.push_back (std::make_pair (id, "Model is missing")); + else if (mModels.searchId(door.mModel) == -1) + messages.push_back (std::make_pair (id, "Model '" + door.mModel + "' does not exist")); // Check that mentioned scripts exist scriptCheck(door, messages, id.toString()); @@ -729,8 +738,6 @@ void CSMTools::ReferenceableCheckStage::npcCheck ( if (npc.mHair.empty()) messages.push_back (std::make_pair (id, "Hair is missing")); // ADD CHECK HERE - //TODO: reputation, Disposition, rank, everything else - // Check inventory inventoryListCheck(npc.mInventory.mList, messages, id.toString()); @@ -871,6 +878,8 @@ void CSMTools::ReferenceableCheckStage::staticCheck ( if (staticElement.mModel.empty()) messages.push_back (std::make_pair (id, "Model is missing")); + else if (mModels.searchId(staticElement.mModel) == -1) + messages.push_back (std::make_pair (id, "Model '" + staticElement.mModel + "' does not exist")); } //final check @@ -890,7 +899,7 @@ void CSMTools::ReferenceableCheckStage::inventoryListCheck( for (size_t i = 0; i < itemList.size(); ++i) { std::string itemName = itemList[i].mItem.toString(); - CSMWorld::RefIdData::LocalIndex localIndex = mReferencables.searchId(itemName); + CSMWorld::RefIdData::LocalIndex localIndex = mObjects.searchId(itemName); if (localIndex.first == -1) messages.push_back (std::make_pair (id, "Item '" + itemName + "' does not exist")); @@ -938,7 +947,9 @@ template void CSMTools::ReferenceableCheckStage::inventoryItemChe //checking for model if (someItem.mModel.empty()) - messages.push_back (std::make_pair (someID, "Model is missing")); // ADD CHECK HERE + messages.push_back (std::make_pair (someID, "Model is missing")); + else if (mModels.searchId(someItem.mModel) == -1) + messages.push_back(std::make_pair(someID, "Model '" + someItem.mModel + "' does not exist")); //checking for icon if (someItem.mIcon.empty()) @@ -964,7 +975,9 @@ template void CSMTools::ReferenceableCheckStage::inventoryItemChe //checking for model if (someItem.mModel.empty()) - messages.push_back (std::make_pair (someID, "Model is missing")); // ADD CHECK HERE + messages.push_back (std::make_pair (someID, "Model is missing")); + else if (mModels.searchId(someItem.mModel) == -1) + messages.push_back (std::make_pair (someID, "Model '" + someItem.mModel + "' does not exist")); //checking for icon if (someItem.mIcon.empty()) @@ -993,7 +1006,7 @@ template void CSMTools::ReferenceableCheckStage::listCheck ( { for (unsigned i = 0; i < someList.mList.size(); ++i) { - if (mReferencables.searchId(someList.mList[i].mId).first == -1) + if (mObjects.searchId(someList.mList[i].mId).first == -1) messages.push_back (std::make_pair (someID, "Object '" + someList.mList[i].mId + "' does not exist")); if (someList.mList[i].mLevel < 1) diff --git a/apps/opencs/model/tools/referenceablecheck.hpp b/apps/opencs/model/tools/referenceablecheck.hpp index f9341bd9c..24f73176c 100644 --- a/apps/opencs/model/tools/referenceablecheck.hpp +++ b/apps/opencs/model/tools/referenceablecheck.hpp @@ -5,6 +5,7 @@ #include "../doc/stage.hpp" #include "../world/data.hpp" #include "../world/refiddata.hpp" +#include "../world/resources.hpp" namespace CSMTools { @@ -16,7 +17,8 @@ namespace CSMTools const CSMWorld::IdCollection& races, const CSMWorld::IdCollection& classes, const CSMWorld::IdCollection& factions, - const CSMWorld::IdCollection& scripts); + const CSMWorld::IdCollection& scripts, + const CSMWorld::Resources& models); virtual void perform(int stage, CSMDoc::Messages& messages); virtual int setup(); @@ -76,11 +78,12 @@ namespace CSMTools CSMDoc::Messages& messages, const std::string& someID); - const CSMWorld::RefIdData& mReferencables; + const CSMWorld::RefIdData& mObjects; const CSMWorld::IdCollection& mRaces; const CSMWorld::IdCollection& mClasses; const CSMWorld::IdCollection& mFactions; const CSMWorld::IdCollection& mScripts; + const CSMWorld::Resources& mModels; bool mPlayerPresent; bool mIgnoreBaseRecords; }; diff --git a/apps/opencs/model/tools/tools.cpp b/apps/opencs/model/tools/tools.cpp index dfa2ae202..2e471d66f 100644 --- a/apps/opencs/model/tools/tools.cpp +++ b/apps/opencs/model/tools/tools.cpp @@ -82,7 +82,8 @@ CSMDoc::OperationHolder *CSMTools::Tools::getVerifier() mVerifierOperation->appendStage (new SpellCheckStage (mData.getSpells())); - mVerifierOperation->appendStage (new ReferenceableCheckStage (mData.getReferenceables().getDataSet(), mData.getRaces(), mData.getClasses(), mData.getFactions(), mData.getScripts())); + mVerifierOperation->appendStage (new ReferenceableCheckStage (mData.getReferenceables().getDataSet(), mData.getRaces(), mData.getClasses(), mData.getFactions(), mData.getScripts(), + mData.getResources (CSMWorld::UniversalId::Type_Meshes))); mVerifierOperation->appendStage (new ReferenceCheckStage(mData.getReferences(), mData.getReferenceables(), mData.getCells(), mData.getFactions())); From 34077a6987939791ffd5f62552eaf6b28194f781 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 25 Aug 2018 01:27:10 +0300 Subject: [PATCH 020/163] Purge unnecessary namespaces --- apps/opencs/model/tools/birthsigncheck.cpp | 1 - apps/opencs/model/tools/birthsigncheck.hpp | 6 +----- apps/opencs/model/tools/magiceffectcheck.cpp | 1 - apps/opencs/model/tools/magiceffectcheck.hpp | 6 +----- 4 files changed, 2 insertions(+), 12 deletions(-) diff --git a/apps/opencs/model/tools/birthsigncheck.cpp b/apps/opencs/model/tools/birthsigncheck.cpp index 4ef264f65..b61bf7421 100644 --- a/apps/opencs/model/tools/birthsigncheck.cpp +++ b/apps/opencs/model/tools/birthsigncheck.cpp @@ -9,7 +9,6 @@ #include "../prefs/state.hpp" #include "../world/data.hpp" -#include "../world/resources.hpp" #include "../world/universalid.hpp" namespace diff --git a/apps/opencs/model/tools/birthsigncheck.hpp b/apps/opencs/model/tools/birthsigncheck.hpp index cd22fb6d0..d032d3cef 100644 --- a/apps/opencs/model/tools/birthsigncheck.hpp +++ b/apps/opencs/model/tools/birthsigncheck.hpp @@ -4,14 +4,10 @@ #include #include "../world/idcollection.hpp" +#include "../world/resources.hpp" #include "../doc/stage.hpp" -namespace CSMWorld -{ - class Resources; -} - namespace CSMTools { /// \brief VerifyStage: make sure that birthsign records are internally consistent diff --git a/apps/opencs/model/tools/magiceffectcheck.cpp b/apps/opencs/model/tools/magiceffectcheck.cpp index 7e7a09aa5..77a26c596 100644 --- a/apps/opencs/model/tools/magiceffectcheck.cpp +++ b/apps/opencs/model/tools/magiceffectcheck.cpp @@ -4,7 +4,6 @@ #include "../prefs/state.hpp" -#include "../world/resources.hpp" #include "../world/data.hpp" namespace diff --git a/apps/opencs/model/tools/magiceffectcheck.hpp b/apps/opencs/model/tools/magiceffectcheck.hpp index 3e45495c1..22dad3db9 100644 --- a/apps/opencs/model/tools/magiceffectcheck.hpp +++ b/apps/opencs/model/tools/magiceffectcheck.hpp @@ -6,14 +6,10 @@ #include "../world/idcollection.hpp" #include "../world/refidcollection.hpp" +#include "../world/resources.hpp" #include "../doc/stage.hpp" -namespace CSMWorld -{ - class Resources; -} - namespace CSMTools { /// \brief VerifyStage: make sure that magic effect records are internally consistent From dc847dce09aeddbe7df925d305dc96969220538d Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 25 Aug 2018 01:44:46 +0300 Subject: [PATCH 021/163] Add item icon checks to object record verifier --- .../opencs/model/tools/referenceablecheck.cpp | 21 ++++++++++++++++--- .../opencs/model/tools/referenceablecheck.hpp | 4 +++- apps/opencs/model/tools/tools.cpp | 2 +- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index 8a1103c3d..d3901ac88 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -1,6 +1,7 @@ #include "referenceablecheck.hpp" #include +#include #include "../prefs/state.hpp" @@ -12,13 +13,15 @@ CSMTools::ReferenceableCheckStage::ReferenceableCheckStage( const CSMWorld::IdCollection& classes, const CSMWorld::IdCollection& faction, const CSMWorld::IdCollection& scripts, - const CSMWorld::Resources& models) + const CSMWorld::Resources& models, + const CSMWorld::Resources& icons) :mObjects(referenceable), mRaces(races), mClasses(classes), mFactions(faction), mScripts(scripts), mModels(models), + mIcons(icons), mPlayerPresent(false) { mIgnoreBaseRecords = false; @@ -953,7 +956,13 @@ template void CSMTools::ReferenceableCheckStage::inventoryItemChe //checking for icon if (someItem.mIcon.empty()) - messages.push_back (std::make_pair (someID, "Icon is missing")); // ADD CHECK HERE + messages.push_back (std::make_pair (someID, "Icon is missing")); + else if (mIcons.searchId(someItem.mIcon) == -1) + { + std::string ddsIcon = someItem.mIcon; + if (!(Misc::ResourceHelpers::changeExtensionToDds(ddsIcon) && mIcons.searchId(ddsIcon) != -1)) + messages.push_back(std::make_pair(someID, "Icon '" + someItem.mIcon + "' does not exist")); + } if (enchantable && someItem.mData.mEnchant < 0) messages.push_back (std::make_pair (someID, "Enchantment points number is negative")); @@ -981,7 +990,13 @@ template void CSMTools::ReferenceableCheckStage::inventoryItemChe //checking for icon if (someItem.mIcon.empty()) - messages.push_back (std::make_pair (someID, "Icon is missing")); // ADD CHECK HERE + messages.push_back (std::make_pair (someID, "Icon is missing")); + else if (mIcons.searchId(someItem.mIcon) == -1) + { + std::string ddsIcon = someItem.mIcon; + if (!(Misc::ResourceHelpers::changeExtensionToDds(ddsIcon) && mIcons.searchId(ddsIcon) != -1)) + messages.push_back(std::make_pair(someID, "Icon '" + someItem.mIcon + "' does not exist")); + } } template void CSMTools::ReferenceableCheckStage::toolCheck ( diff --git a/apps/opencs/model/tools/referenceablecheck.hpp b/apps/opencs/model/tools/referenceablecheck.hpp index 24f73176c..5da737875 100644 --- a/apps/opencs/model/tools/referenceablecheck.hpp +++ b/apps/opencs/model/tools/referenceablecheck.hpp @@ -18,7 +18,8 @@ namespace CSMTools const CSMWorld::IdCollection& classes, const CSMWorld::IdCollection& factions, const CSMWorld::IdCollection& scripts, - const CSMWorld::Resources& models); + const CSMWorld::Resources& models, + const CSMWorld::Resources& icons); virtual void perform(int stage, CSMDoc::Messages& messages); virtual int setup(); @@ -84,6 +85,7 @@ namespace CSMTools const CSMWorld::IdCollection& mFactions; const CSMWorld::IdCollection& mScripts; const CSMWorld::Resources& mModels; + const CSMWorld::Resources& mIcons; bool mPlayerPresent; bool mIgnoreBaseRecords; }; diff --git a/apps/opencs/model/tools/tools.cpp b/apps/opencs/model/tools/tools.cpp index 2e471d66f..87b5041e0 100644 --- a/apps/opencs/model/tools/tools.cpp +++ b/apps/opencs/model/tools/tools.cpp @@ -83,7 +83,7 @@ CSMDoc::OperationHolder *CSMTools::Tools::getVerifier() mVerifierOperation->appendStage (new SpellCheckStage (mData.getSpells())); mVerifierOperation->appendStage (new ReferenceableCheckStage (mData.getReferenceables().getDataSet(), mData.getRaces(), mData.getClasses(), mData.getFactions(), mData.getScripts(), - mData.getResources (CSMWorld::UniversalId::Type_Meshes))); + mData.getResources (CSMWorld::UniversalId::Type_Meshes), mData.getResources (CSMWorld::UniversalId::Type_Icons))); mVerifierOperation->appendStage (new ReferenceCheckStage(mData.getReferences(), mData.getReferenceables(), mData.getCells(), mData.getFactions())); From 5b8880c6fa24b65f6899a9141a0f64fd991368e9 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 25 Aug 2018 02:03:55 +0300 Subject: [PATCH 022/163] Update cell reference record verifier messages --- apps/opencs/model/tools/referencecheck.cpp | 42 +++++++++++----------- apps/opencs/model/tools/referencecheck.hpp | 2 +- 2 files changed, 21 insertions(+), 23 deletions(-) diff --git a/apps/opencs/model/tools/referencecheck.cpp b/apps/opencs/model/tools/referencecheck.cpp index 447238be4..dd93b4e9c 100644 --- a/apps/opencs/model/tools/referencecheck.cpp +++ b/apps/opencs/model/tools/referencecheck.cpp @@ -9,7 +9,7 @@ CSMTools::ReferenceCheckStage::ReferenceCheckStage( const CSMWorld::IdCollection& factions) : mReferences(references), - mReferencables(referencables), + mObjects(referencables), mDataSet(referencables.getDataSet()), mCells(cells), mFactions(factions) @@ -30,75 +30,73 @@ void CSMTools::ReferenceCheckStage::perform(int stage, CSMDoc::Messages &message // Check for empty reference id if (cellRef.mRefID.empty()) { - messages.push_back(std::make_pair(id, " is an empty instance (not based on an object)")); + messages.push_back(std::make_pair(id, "Instance is not based on an object")); } else { // Check for non existing referenced object - if (mReferencables.searchId(cellRef.mRefID) == -1) { - messages.push_back(std::make_pair(id, " is referencing non existing object " + cellRef.mRefID)); + if (mObjects.searchId(cellRef.mRefID) == -1) { + messages.push_back(std::make_pair(id, "Instance of a non-existent object '" + cellRef.mRefID + "'")); } else { // Check if reference charge is valid for it's proper referenced type CSMWorld::RefIdData::LocalIndex localIndex = mDataSet.searchId(cellRef.mRefID); bool isLight = localIndex.second == CSMWorld::UniversalId::Type_Light; if ((isLight && cellRef.mChargeFloat < -1) || (!isLight && cellRef.mChargeInt < -1)) { - std::string str = " has invalid charge "; + std::string str = "Invalid charge: "; if (localIndex.second == CSMWorld::UniversalId::Type_Light) str += std::to_string(cellRef.mChargeFloat); else str += std::to_string(cellRef.mChargeInt); - messages.push_back(std::make_pair(id, id.getId() + str)); + messages.push_back(std::make_pair(id, str)); } } } // If object have owner, check if that owner reference is valid - if (!cellRef.mOwner.empty() && mReferencables.searchId(cellRef.mOwner) == -1) - messages.push_back(std::make_pair(id, " has non existing owner " + cellRef.mOwner)); + if (!cellRef.mOwner.empty() && mObjects.searchId(cellRef.mOwner) == -1) + messages.push_back(std::make_pair(id, "Owner object '" + cellRef.mOwner + "' does not exist")); // If object have creature soul trapped, check if that creature reference is valid if (!cellRef.mSoul.empty()) - if (mReferencables.searchId(cellRef.mSoul) == -1) - messages.push_back(std::make_pair(id, " has non existing trapped soul " + cellRef.mSoul)); + if (mObjects.searchId(cellRef.mSoul) == -1) + messages.push_back(std::make_pair(id, "Trapped soul object '" + cellRef.mSoul + "' does not exist")); bool hasFaction = !cellRef.mFaction.empty(); // If object have faction, check if that faction reference is valid if (hasFaction) if (mFactions.searchId(cellRef.mFaction) == -1) - messages.push_back(std::make_pair(id, " has non existing faction " + cellRef.mFaction)); + messages.push_back(std::make_pair(id, "Faction '" + cellRef.mFaction + "' does not exist")); // Check item's faction rank - if (hasFaction && cellRef.mFactionRank < -1) - messages.push_back(std::make_pair(id, " has faction set but has invalid faction rank " + std::to_string(cellRef.mFactionRank))); - else if (!hasFaction && cellRef.mFactionRank != -2) - messages.push_back(std::make_pair(id, " has invalid faction rank " + std::to_string(cellRef.mFactionRank))); + if ((hasFaction && cellRef.mFactionRank < -1) || (!hasFaction && cellRef.mFactionRank != -2)) + messages.push_back(std::make_pair(id, "Invalid faction rank " + std::to_string(cellRef.mFactionRank))); // If door have destination cell, check if that reference is valid if (!cellRef.mDestCell.empty()) if (mCells.searchId(cellRef.mDestCell) == -1) - messages.push_back(std::make_pair(id, " has non existing destination cell " + cellRef.mDestCell)); + messages.push_back(std::make_pair(id, "Destination cell '" + cellRef.mDestCell + "' does not exist")); // Check if scale isn't negative if (cellRef.mScale < 0) { - std::string str = " has negative scale "; + std::string str = "Negative scale: "; str += std::to_string(cellRef.mScale); - messages.push_back(std::make_pair(id, id.getId() + str)); + messages.push_back(std::make_pair(id, str)); } // Check if enchantement points aren't negative or are at full (-1) if (cellRef.mEnchantmentCharge < 0 && cellRef.mEnchantmentCharge != -1) { - std::string str = " has negative enchantment points "; + std::string str = "Negative enchantment points: "; str += std::to_string(cellRef.mEnchantmentCharge); - messages.push_back(std::make_pair(id, id.getId() + str)); + messages.push_back(std::make_pair(id, str)); } // Check if gold value isn't negative if (cellRef.mGoldValue < 0) { - std::string str = " has negative gold value "; + std::string str = "Negative gold value: "; str += cellRef.mGoldValue; - messages.push_back(std::make_pair(id, id.getId() + str)); + messages.push_back(std::make_pair(id, str)); } } diff --git a/apps/opencs/model/tools/referencecheck.hpp b/apps/opencs/model/tools/referencecheck.hpp index 5e25924f3..7373903e6 100644 --- a/apps/opencs/model/tools/referencecheck.hpp +++ b/apps/opencs/model/tools/referencecheck.hpp @@ -19,7 +19,7 @@ namespace CSMTools private: const CSMWorld::RefCollection& mReferences; - const CSMWorld::RefIdCollection& mReferencables; + const CSMWorld::RefIdCollection& mObjects; const CSMWorld::RefIdData& mDataSet; const CSMWorld::IdCollection& mCells; const CSMWorld::IdCollection& mFactions; From 6b226eef8f53d06dfdb9bb0817614ee4db35077b Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 25 Aug 2018 02:38:02 +0300 Subject: [PATCH 023/163] Use Messages::add in magic effect record verifier --- apps/opencs/model/tools/magiceffectcheck.cpp | 128 ++++++++++++------- 1 file changed, 81 insertions(+), 47 deletions(-) diff --git a/apps/opencs/model/tools/magiceffectcheck.cpp b/apps/opencs/model/tools/magiceffectcheck.cpp index 77a26c596..917e358c3 100644 --- a/apps/opencs/model/tools/magiceffectcheck.cpp +++ b/apps/opencs/model/tools/magiceffectcheck.cpp @@ -4,23 +4,8 @@ #include "../prefs/state.hpp" -#include "../world/data.hpp" - -namespace -{ - void addMessage(CSMDoc::Messages &messages, const CSMWorld::UniversalId &id, const std::string& text) - { - if (!text.empty()) - { - messages.push_back(std::make_pair(id, text)); - } - } -} - std::string CSMTools::MagicEffectCheckStage::checkTexture(const std::string &texture, bool isIcon) const { - if (texture.empty()) return (isIcon ? "Icon is missing" : std::string()); - const CSMWorld::Resources &textures = isIcon ? mIcons : mTextures; if (textures.searchId(texture) != -1) return std::string(); @@ -34,30 +19,16 @@ std::string CSMTools::MagicEffectCheckStage::checkObject(const std::string &id, const CSMWorld::UniversalId &type, const std::string &column) const { - std::string error; - if (!id.empty()) - { - CSMWorld::RefIdData::LocalIndex index = mObjects.getDataSet().searchId(id); - if (index.first == -1) - { - error = column + " '" + id + "' " + "does not exist"; - } - else if (index.second != type.getType()) - { - error = column + " '" + id + "' " + "does not have " + type.getTypeName() + " type"; - } - } - return error; + CSMWorld::RefIdData::LocalIndex index = mObjects.getDataSet().searchId(id); + if (index.first == -1) return (column + " '" + id + "' " + "does not exist"); + else if (index.second != type.getType()) return (column + " '" + id + "' " + "does not have " + type.getTypeName() + " type"); + return std::string(); } std::string CSMTools::MagicEffectCheckStage::checkSound(const std::string &id, const std::string &column) const { - std::string error; - if (!id.empty() && mSounds.searchId(id) == -1) - { - error = column + " '" + id + "' " + "does not exist"; - } - return error; + if (!id.empty() && mSounds.searchId(id) == -1) return (column + " '" + id + "' " + "does not exist"); + return std::string(); } CSMTools::MagicEffectCheckStage::MagicEffectCheckStage(const CSMWorld::IdCollection &effects, @@ -94,22 +65,85 @@ void CSMTools::MagicEffectCheckStage::perform(int stage, CSMDoc::Messages &messa if (effect.mDescription.empty()) { - addMessage(messages, id, "Description is missing"); + messages.add(id, "Description is missing", "", CSMDoc::Message::Severity_Warning); } if (effect.mData.mBaseCost < 0.0f) { - addMessage(messages, id, "Base cost is negative"); + messages.add(id, "Base cost is negative", "", CSMDoc::Message::Severity_Error); } - addMessage(messages, id, checkTexture(effect.mIcon, true)); - addMessage(messages, id, checkTexture(effect.mParticle, false)); - addMessage(messages, id, checkObject(effect.mCasting, CSMWorld::UniversalId::Type_Static, "Casting object")); - addMessage(messages, id, checkObject(effect.mHit, CSMWorld::UniversalId::Type_Static, "Hit object")); - addMessage(messages, id, checkObject(effect.mArea, CSMWorld::UniversalId::Type_Static, "Area object")); - addMessage(messages, id, checkObject(effect.mBolt, CSMWorld::UniversalId::Type_Weapon, "Bolt object")); - addMessage(messages, id, checkSound(effect.mCastSound, "Casting sound")); - addMessage(messages, id, checkSound(effect.mHitSound, "Hit sound")); - addMessage(messages, id, checkSound(effect.mAreaSound, "Area sound")); - addMessage(messages, id, checkSound(effect.mBoltSound, "Bolt sound")); + if (effect.mIcon.empty()) + { + messages.add(id, "Icon is missing", "", CSMDoc::Message::Severity_Error); + } + else + { + const std::string error = checkTexture(effect.mIcon, true); + if (!error.empty()) + messages.add(id, error, "", CSMDoc::Message::Severity_Error); + } + + if (!effect.mParticle.empty()) + { + const std::string error = checkTexture(effect.mParticle, false); + if (!error.empty()) + messages.add(id, error, "", CSMDoc::Message::Severity_Error); + } + + if (!effect.mCasting.empty()) + { + const std::string error = checkObject(effect.mCasting, CSMWorld::UniversalId::Type_Static, "Casting object"); + if (!error.empty()) + messages.add(id, error, "", CSMDoc::Message::Severity_Error); + } + + if (!effect.mHit.empty()) + { + const std::string error = checkObject(effect.mHit, CSMWorld::UniversalId::Type_Static, "Hit object"); + if (!error.empty()) + messages.add(id, error, "", CSMDoc::Message::Severity_Error); + } + + if (!effect.mArea.empty()) + { + const std::string error = checkObject(effect.mArea, CSMWorld::UniversalId::Type_Static, "Area object"); + if (!error.empty()) + messages.add(id, error, "", CSMDoc::Message::Severity_Error); + } + + if (!effect.mBolt.empty()) + { + const std::string error = checkObject(effect.mBolt, CSMWorld::UniversalId::Type_Static, "Bolt object"); + if (!error.empty()) + messages.add(id, error, "", CSMDoc::Message::Severity_Error); + } + + if (!effect.mCastSound.empty()) + { + const std::string error = checkSound(effect.mCastSound, "Casting sound"); + if (!error.empty()) + messages.add(id, error, "", CSMDoc::Message::Severity_Error); + } + + if (!effect.mHitSound.empty()) + { + const std::string error = checkSound(effect.mHitSound, "Hit sound"); + if (!error.empty()) + messages.add(id, error, "", CSMDoc::Message::Severity_Error); + } + + if (!effect.mAreaSound.empty()) + { + const std::string error = checkSound(effect.mAreaSound, "Area sound"); + if (!error.empty()) + messages.add(id, error, "", CSMDoc::Message::Severity_Error); + } + + if (!effect.mBoltSound.empty()) + { + const std::string error = checkSound(effect.mBoltSound, "Bolt sound"); + if (!error.empty()) + messages.add(id, error, "", CSMDoc::Message::Severity_Error); + } } From d433929194578c477343df7a09183b5ed83dde20 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 25 Aug 2018 02:48:58 +0300 Subject: [PATCH 024/163] Use Messages::add in birthsign record verifier --- apps/opencs/model/tools/birthsigncheck.cpp | 37 ++++++++++------------ 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/apps/opencs/model/tools/birthsigncheck.cpp b/apps/opencs/model/tools/birthsigncheck.cpp index b61bf7421..c582da8e4 100644 --- a/apps/opencs/model/tools/birthsigncheck.cpp +++ b/apps/opencs/model/tools/birthsigncheck.cpp @@ -1,37 +1,21 @@ #include "birthsigncheck.hpp" -#include -#include - #include #include #include "../prefs/state.hpp" -#include "../world/data.hpp" #include "../world/universalid.hpp" -namespace -{ - void addMessage(CSMDoc::Messages &messages, const CSMWorld::UniversalId &id, const std::string& text) - { - if (!text.empty()) - { - messages.push_back(std::make_pair(id, text)); - } - } -} - std::string CSMTools::BirthsignCheckStage::checkTexture(const std::string &texture) const { - if (texture.empty()) return "Texture is missing"; if (mTextures.searchId(texture) != -1) return std::string(); std::string ddsTexture = texture; if (Misc::ResourceHelpers::changeExtensionToDds(ddsTexture) && mTextures.searchId(ddsTexture) != -1) return std::string(); - return "Texture '" + texture + "' does not exist"; + return "Image '" + texture + "' does not exist"; } CSMTools::BirthsignCheckStage::BirthsignCheckStage (const CSMWorld::IdCollection& birthsigns, @@ -62,12 +46,25 @@ void CSMTools::BirthsignCheckStage::perform (int stage, CSMDoc::Messages& messag CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Birthsign, birthsign.mId); if (birthsign.mName.empty()) - addMessage(messages, id, "Name is missing"); + { + messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Error); + } if (birthsign.mDescription.empty()) - addMessage(messages, id, "Description is missing"); + { + messages.add(id, "Description is missing", "", CSMDoc::Message::Severity_Warning); + } - addMessage(messages, id, checkTexture(birthsign.mTexture)); + if (birthsign.mTexture.empty()) + { + messages.add(id, "Image is missing", "", CSMDoc::Message::Severity_Error); + } + else + { + const std::string error = checkTexture(birthsign.mTexture); + if (!error.empty()) + messages.add(id, error, "", CSMDoc::Message::Severity_Error); + } /// \todo check data members that can't be edited in the table view } From 8dcb58d745839d87272446cb318565cc08756484 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 25 Aug 2018 03:49:10 +0300 Subject: [PATCH 025/163] Use Messages::add in Faction, Sound, Bodypart and Class record verifiers --- apps/opencs/model/tools/bodypartcheck.cpp | 15 +++++++-------- apps/opencs/model/tools/classcheck.cpp | 19 +++++++++---------- apps/opencs/model/tools/factioncheck.cpp | 6 +++--- apps/opencs/model/tools/soundcheck.cpp | 11 +++++------ 4 files changed, 24 insertions(+), 27 deletions(-) diff --git a/apps/opencs/model/tools/bodypartcheck.cpp b/apps/opencs/model/tools/bodypartcheck.cpp index d33705cf6..16dd9891e 100644 --- a/apps/opencs/model/tools/bodypartcheck.cpp +++ b/apps/opencs/model/tools/bodypartcheck.cpp @@ -34,24 +34,23 @@ void CSMTools::BodyPartCheckStage::perform (int stage, CSMDoc::Messages &message // Check BYDT if (bodyPart.mData.mPart > 14 ) - messages.push_back(std::make_pair(id, "Invalid mesh part")); + messages.add(id, "Invalid part", "", CSMDoc::Message::Severity_Error); if (bodyPart.mData.mFlags > 3 ) - messages.push_back(std::make_pair(id, "Invalid flags")); + messages.add(id, "Invalid flags", "", CSMDoc::Message::Severity_Error); if (bodyPart.mData.mType > 2 ) - messages.push_back(std::make_pair(id, "Invalid type")); + messages.add(id, "Invalid type", "", CSMDoc::Message::Severity_Error); // Check MODL - if ( bodyPart.mModel.empty() ) - messages.push_back(std::make_pair(id, "Model is missing" )); + messages.add(id, "Model is missing", "", CSMDoc::Message::Severity_Error); else if ( mMeshes.searchId( bodyPart.mModel ) == -1 ) - messages.push_back(std::make_pair(id, "Model '" + bodyPart.mModel + "' does not exist")); + messages.add(id, "Model '" + bodyPart.mModel + "' does not exist", "", CSMDoc::Message::Severity_Error); // Check FNAM if ( bodyPart.mRace.empty() ) - messages.push_back(std::make_pair(id, "Race is missing" )); + messages.add(id, "Race is missing", "", CSMDoc::Message::Severity_Error); else if ( mRaces.searchId( bodyPart.mRace ) == -1 ) - messages.push_back(std::make_pair(id, "Race '" + bodyPart.mRace + " does not exist")); + messages.add(id, "Race '" + bodyPart.mRace + " does not exist", "", CSMDoc::Message::Severity_Error); } diff --git a/apps/opencs/model/tools/classcheck.cpp b/apps/opencs/model/tools/classcheck.cpp index 157dfab7f..b409c4e06 100644 --- a/apps/opencs/model/tools/classcheck.cpp +++ b/apps/opencs/model/tools/classcheck.cpp @@ -1,6 +1,5 @@ #include "classcheck.hpp" -#include #include #include @@ -37,26 +36,24 @@ void CSMTools::ClassCheckStage::perform (int stage, CSMDoc::Messages& messages) // A class should have a name if (class_.mName.empty()) - messages.push_back (std::make_pair (id, "Name is missing")); + messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Warning); // A playable class should have a description if (class_.mData.mIsPlayable != 0 && class_.mDescription.empty()) - messages.push_back (std::make_pair (id, "Description of a playable class is missing")); + messages.add(id, "Description of a playable class is missing", "", CSMDoc::Message::Severity_Warning); // test for invalid attributes for (int i=0; i<2; ++i) + { if (class_.mData.mAttribute[i]==-1) { - std::ostringstream stream; - - stream << "Attribute #" << i << " is not set"; - - messages.push_back (std::make_pair (id, stream.str())); + messages.add(id, "Attribute #" + std::to_string(i) + " is not set", "", CSMDoc::Message::Severity_Error); } + } if (class_.mData.mAttribute[0]==class_.mData.mAttribute[1] && class_.mData.mAttribute[0]!=-1) { - messages.push_back (std::make_pair (id, "Same attribute is listed twice")); + messages.add(id, "Same attribute is listed twice", "", CSMDoc::Message::Severity_Warning); } // test for non-unique skill @@ -67,8 +64,10 @@ void CSMTools::ClassCheckStage::perform (int stage, CSMDoc::Messages& messages) ++skills[class_.mData.mSkills[i][i2]]; for (auto &skill : skills) + { if (skill.second>1) { - messages.push_back (std::make_pair (id, "Skill " + ESM::Skill::indexToId (skill.first) + " is listed more than once")); + messages.add(id, "Skill " + ESM::Skill::indexToId (skill.first) + " is listed more than once", "", CSMDoc::Message::Severity_Warning); } + } } diff --git a/apps/opencs/model/tools/factioncheck.cpp b/apps/opencs/model/tools/factioncheck.cpp index d19c0ae52..5bd613851 100644 --- a/apps/opencs/model/tools/factioncheck.cpp +++ b/apps/opencs/model/tools/factioncheck.cpp @@ -37,12 +37,12 @@ void CSMTools::FactionCheckStage::perform (int stage, CSMDoc::Messages& messages // test for empty name if (faction.mName.empty()) - messages.push_back (std::make_pair (id, "Name is missing")); + messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Warning); // test for invalid attributes if (faction.mData.mAttribute[0]==faction.mData.mAttribute[1] && faction.mData.mAttribute[0]!=-1) { - messages.push_back (std::make_pair (id, "Same attribute is listed twice")); + messages.add(id, "Same attribute is listed twice", "", CSMDoc::Message::Severity_Warning); } // test for non-unique skill @@ -55,7 +55,7 @@ void CSMTools::FactionCheckStage::perform (int stage, CSMDoc::Messages& messages for (auto &skill : skills) if (skill.second>1) { - messages.push_back (std::make_pair (id, "Skill " + ESM::Skill::indexToId (skill.first) + " is listed more than once")); + messages.add(id, "Skill " + ESM::Skill::indexToId (skill.first) + " is listed more than once", "", CSMDoc::Message::Severity_Warning); } /// \todo check data members that can't be edited in the table view diff --git a/apps/opencs/model/tools/soundcheck.cpp b/apps/opencs/model/tools/soundcheck.cpp index 5708a59d3..7bb27e984 100644 --- a/apps/opencs/model/tools/soundcheck.cpp +++ b/apps/opencs/model/tools/soundcheck.cpp @@ -1,12 +1,9 @@ #include "soundcheck.hpp" -#include - #include #include "../prefs/state.hpp" -#include "../world/data.hpp" #include "../world/resources.hpp" #include "../world/universalid.hpp" @@ -38,14 +35,16 @@ void CSMTools::SoundCheckStage::perform (int stage, CSMDoc::Messages& messages) CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Sound, sound.mId); if (sound.mData.mMinRange>sound.mData.mMaxRange) - messages.push_back (std::make_pair (id, "Minimum range larger than maximum range")); + { + messages.add(id, "Minimum range larger than maximum range", "", CSMDoc::Message::Severity_Warning); + } if (sound.mSound.empty()) { - messages.push_back(std::make_pair(id, "Sound file is missing")); + messages.add(id, "Sound file is missing", "", CSMDoc::Message::Severity_Error); } else if (mSoundFiles.searchId(sound.mSound) == -1) { - messages.push_back(std::make_pair(id, "Sound file '" + sound.mSound + "' does not exist")); + messages.add(id, "Sound file '" + sound.mSound + "' does not exist", "", CSMDoc::Message::Severity_Error); } } From b2b9bd94f09b817a20ac9cae3b54e7f174bd4f6d Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 25 Aug 2018 04:05:48 +0300 Subject: [PATCH 026/163] Fix bolt object type --- apps/opencs/model/tools/magiceffectcheck.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/opencs/model/tools/magiceffectcheck.cpp b/apps/opencs/model/tools/magiceffectcheck.cpp index 917e358c3..943d89cdc 100644 --- a/apps/opencs/model/tools/magiceffectcheck.cpp +++ b/apps/opencs/model/tools/magiceffectcheck.cpp @@ -114,7 +114,7 @@ void CSMTools::MagicEffectCheckStage::perform(int stage, CSMDoc::Messages &messa if (!effect.mBolt.empty()) { - const std::string error = checkObject(effect.mBolt, CSMWorld::UniversalId::Type_Static, "Bolt object"); + const std::string error = checkObject(effect.mBolt, CSMWorld::UniversalId::Type_Weapon, "Bolt object"); if (!error.empty()) messages.add(id, error, "", CSMDoc::Message::Severity_Error); } From 35281d7c3844b9c3b1af100fd7317d8ca9eebc78 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 25 Aug 2018 05:04:49 +0300 Subject: [PATCH 027/163] Use Messages::add in race, soundgen, spell and start script record verifiers --- apps/opencs/model/tools/racecheck.cpp | 18 ++++++++++-------- apps/opencs/model/tools/soundgencheck.cpp | 8 ++++---- apps/opencs/model/tools/spellcheck.cpp | 6 +++--- apps/opencs/model/tools/startscriptcheck.cpp | 2 +- 4 files changed, 18 insertions(+), 16 deletions(-) diff --git a/apps/opencs/model/tools/racecheck.cpp b/apps/opencs/model/tools/racecheck.cpp index 7ce9a8869..3b487b925 100644 --- a/apps/opencs/model/tools/racecheck.cpp +++ b/apps/opencs/model/tools/racecheck.cpp @@ -28,25 +28,27 @@ void CSMTools::RaceCheckStage::performPerRecord (int stage, CSMDoc::Messages& me CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Race, race.mId); // test for empty name and description - if (race.mName.empty()) - messages.push_back (std::make_pair (id, "Name is missing")); + if (race.mName.empty() && race.mData.mFlags & 0x1) + messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Error); + else if (race.mName.empty()) + messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Warning); if (race.mDescription.empty()) - messages.push_back (std::make_pair (id, "Description is missing")); + messages.add(id, "Description is missing", "", CSMDoc::Message::Severity_Warning); // test for positive height if (race.mData.mHeight.mMale<=0) - messages.push_back (std::make_pair (id, "Male height is non-positive")); + messages.add(id, "Male height is non-positive", "", CSMDoc::Message::Severity_Error); if (race.mData.mHeight.mFemale<=0) - messages.push_back (std::make_pair (id, "Female height is non-positive")); + messages.add(id, "Female height is non-positive", "", CSMDoc::Message::Severity_Error); // test for non-negative weight if (race.mData.mWeight.mMale<0) - messages.push_back (std::make_pair (id, "Male weight is negative")); + messages.add(id, "Male weight is negative", "", CSMDoc::Message::Severity_Error); if (race.mData.mWeight.mFemale<0) - messages.push_back (std::make_pair (id, "Female weight is negative")); + messages.add(id, "Female weight is negative", "", CSMDoc::Message::Severity_Error); /// \todo check data members that can't be edited in the table view } @@ -56,7 +58,7 @@ void CSMTools::RaceCheckStage::performFinal (CSMDoc::Messages& messages) CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Races); if (!mPlayable) - messages.push_back (std::make_pair (id, "No playable race")); + messages.add(id, "No playable race", "", CSMDoc::Message::Severity_SeriousError); } CSMTools::RaceCheckStage::RaceCheckStage (const CSMWorld::IdCollection& races) diff --git a/apps/opencs/model/tools/soundgencheck.cpp b/apps/opencs/model/tools/soundgencheck.cpp index 99a8c184c..7b6cf7a4b 100644 --- a/apps/opencs/model/tools/soundgencheck.cpp +++ b/apps/opencs/model/tools/soundgencheck.cpp @@ -40,20 +40,20 @@ void CSMTools::SoundGenCheckStage::perform(int stage, CSMDoc::Messages &messages CSMWorld::RefIdData::LocalIndex creatureIndex = mObjects.getDataSet().searchId(soundGen.mCreature); if (creatureIndex.first == -1) { - messages.push_back(std::make_pair(id, "Creature '" + soundGen.mCreature + "' doesn't exist")); + messages.add(id, "Creature '" + soundGen.mCreature + "' doesn't exist", "", CSMDoc::Message::Severity_Error); } else if (creatureIndex.second != CSMWorld::UniversalId::Type_Creature) { - messages.push_back(std::make_pair(id, "'" + soundGen.mCreature + "' is not a creature")); + messages.add(id, "'" + soundGen.mCreature + "' is not a creature", "", CSMDoc::Message::Severity_Error); } } if (soundGen.mSound.empty()) { - messages.push_back(std::make_pair(id, "Sound is missing")); + messages.add(id, "Sound is missing", "", CSMDoc::Message::Severity_Error); } else if (mSounds.searchId(soundGen.mSound) == -1) { - messages.push_back(std::make_pair(id, "Sound '" + soundGen.mSound + "' doesn't exist")); + messages.add(id, "Sound '" + soundGen.mSound + "' doesn't exist", "", CSMDoc::Message::Severity_Error); } } diff --git a/apps/opencs/model/tools/spellcheck.cpp b/apps/opencs/model/tools/spellcheck.cpp index 6c08aca37..dc9ce65c0 100644 --- a/apps/opencs/model/tools/spellcheck.cpp +++ b/apps/opencs/model/tools/spellcheck.cpp @@ -34,13 +34,13 @@ void CSMTools::SpellCheckStage::perform (int stage, CSMDoc::Messages& messages) CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Spell, spell.mId); - // test for empty name and description + // test for empty name if (spell.mName.empty()) - messages.push_back (std::make_pair (id, "Name is missing")); + messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Error); // test for invalid cost values if (spell.mData.mCost<0) - messages.push_back (std::make_pair (id, "Spell cost is negative")); + messages.add(id, "Spell cost is negative", "", CSMDoc::Message::Severity_Error); /// \todo check data members that can't be edited in the table view } diff --git a/apps/opencs/model/tools/startscriptcheck.cpp b/apps/opencs/model/tools/startscriptcheck.cpp index 5a9f63dc4..deb7d384f 100644 --- a/apps/opencs/model/tools/startscriptcheck.cpp +++ b/apps/opencs/model/tools/startscriptcheck.cpp @@ -25,7 +25,7 @@ void CSMTools::StartScriptCheckStage::perform(int stage, CSMDoc::Messages& messa CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_StartScript, scriptId); if (mScripts.searchId (Misc::StringUtils::lowerCase (scriptId))==-1) - messages.push_back (std::make_pair (id, "Start script " + scriptId + " does not exist")); + messages.add(id, "Start script " + scriptId + " does not exist", "", CSMDoc::Message::Severity_Error); } int CSMTools::StartScriptCheckStage::setup() From 1e5330d9da8577df523f7c51c9dda94bc036d27f Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 25 Aug 2018 05:16:49 +0300 Subject: [PATCH 028/163] Use Messages::add in skill record verifier --- apps/opencs/model/tools/skillcheck.cpp | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/apps/opencs/model/tools/skillcheck.cpp b/apps/opencs/model/tools/skillcheck.cpp index a2bf3ff03..ab7df51cb 100644 --- a/apps/opencs/model/tools/skillcheck.cpp +++ b/apps/opencs/model/tools/skillcheck.cpp @@ -1,7 +1,5 @@ #include "skillcheck.hpp" -#include - #include #include "../prefs/state.hpp" @@ -33,16 +31,14 @@ void CSMTools::SkillCheckStage::perform (int stage, CSMDoc::Messages& messages) CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Skill, skill.mId); + if (skill.mDescription.empty()) + messages.add(id, "Description is missing", "", CSMDoc::Message::Severity_Warning); + for (int i=0; i<4; ++i) + { if (skill.mData.mUseValue[i]<0) { - std::ostringstream stream; - - stream << "Usage experience value #" << i << " is negative"; - - messages.push_back (std::make_pair (id, stream.str())); + messages.add(id, "Usage experience value #" + std::to_string(i) + " is negative", "", CSMDoc::Message::Severity_Error); } - - if (skill.mDescription.empty()) - messages.push_back (std::make_pair (id, "Description is missing")); + } } From e1ae7a9b0e964154cc0a205ad1bc14364d0f7a27 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 25 Aug 2018 06:40:14 +0300 Subject: [PATCH 029/163] Avoid duplicate duplicate pathgrid point warnings --- apps/opencs/model/tools/pathgridcheck.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/apps/opencs/model/tools/pathgridcheck.cpp b/apps/opencs/model/tools/pathgridcheck.cpp index e63e9122f..88750ad7f 100644 --- a/apps/opencs/model/tools/pathgridcheck.cpp +++ b/apps/opencs/model/tools/pathgridcheck.cpp @@ -100,11 +100,8 @@ void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& message // check duplicate points // FIXME: how to do this efficiently? - for (unsigned int j = 0; j < pathgrid.mPoints.size(); ++j) + for (unsigned int j = 0; j != i; ++j) { - if (j == i) - continue; - if (pathgrid.mPoints[i].mX == pathgrid.mPoints[j].mX && pathgrid.mPoints[i].mY == pathgrid.mPoints[j].mY && pathgrid.mPoints[i].mZ == pathgrid.mPoints[j].mZ) From 85dc1e4ef2b43953fbade3294acb5a77a6242c28 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 26 Aug 2018 10:45:02 +0300 Subject: [PATCH 030/163] Revert unnecessary universalid changes in Journal record verifier --- apps/opencs/model/tools/journalcheck.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/opencs/model/tools/journalcheck.cpp b/apps/opencs/model/tools/journalcheck.cpp index bd77e95b6..dc15b26ee 100644 --- a/apps/opencs/model/tools/journalcheck.cpp +++ b/apps/opencs/model/tools/journalcheck.cpp @@ -33,7 +33,6 @@ void CSMTools::JournalCheckStage::perform(int stage, CSMDoc::Messages& messages) std::set questIndices; CSMWorld::InfoCollection::Range range = mJournalInfos.getTopicRange(journal.mId); - CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Journal, journal.mId); for (CSMWorld::InfoCollection::RecordConstIterator it = range.first; it != range.second; ++it) { @@ -57,6 +56,7 @@ void CSMTools::JournalCheckStage::perform(int stage, CSMDoc::Messages& messages) if (journalInfo.mResponse.empty()) { + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_JournalInfo, journalInfo.mId); messages.add(id, "Missing journal entry text", "", CSMDoc::Message::Severity_Warning); } @@ -65,6 +65,7 @@ void CSMTools::JournalCheckStage::perform(int stage, CSMDoc::Messages& messages) // Duplicate index if (result.second == false) { + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_JournalInfo, journalInfo.mId); std::ostringstream stream; stream << "Duplicated quest index " << journalInfo.mData.mJournalIndex; messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); @@ -73,10 +74,12 @@ void CSMTools::JournalCheckStage::perform(int stage, CSMDoc::Messages& messages) if (totalInfoCount == 0) { + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Journal, journal.mId); messages.add(id, "No related journal entry", "", CSMDoc::Message::Severity_Warning); } else if (statusNamedCount > 1) { + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Journal, journal.mId); messages.add(id, "Multiple entries with quest status 'Named'", "", CSMDoc::Message::Severity_Error); } } From d1b2fc11ef58594bacafe48b3191e9bf150f8983 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 26 Aug 2018 13:00:39 +0300 Subject: [PATCH 031/163] Use messages::add in object record verifiers Add NPC head and hair body part existence checks and expand creature record verifier, update playable class checks in class record verifier --- apps/opencs/model/tools/classcheck.cpp | 7 +- .../opencs/model/tools/referenceablecheck.cpp | 263 +++++++++--------- .../opencs/model/tools/referenceablecheck.hpp | 4 +- apps/opencs/model/tools/tools.cpp | 3 +- 4 files changed, 147 insertions(+), 130 deletions(-) diff --git a/apps/opencs/model/tools/classcheck.cpp b/apps/opencs/model/tools/classcheck.cpp index b409c4e06..9984c3287 100644 --- a/apps/opencs/model/tools/classcheck.cpp +++ b/apps/opencs/model/tools/classcheck.cpp @@ -36,7 +36,12 @@ void CSMTools::ClassCheckStage::perform (int stage, CSMDoc::Messages& messages) // A class should have a name if (class_.mName.empty()) - messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Warning); + { + if (class_.mData.mIsPlayable != 0) + messages.add(id, "Name of a playable class is missing", "", CSMDoc::Message::Severity_Error); + else + messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Warning); + } // A playable class should have a description if (class_.mData.mIsPlayable != 0 && class_.mDescription.empty()) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index d3901ac88..c61d84d51 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -14,7 +14,8 @@ CSMTools::ReferenceableCheckStage::ReferenceableCheckStage( const CSMWorld::IdCollection& faction, const CSMWorld::IdCollection& scripts, const CSMWorld::Resources& models, - const CSMWorld::Resources& icons) + const CSMWorld::Resources& icons, + const CSMWorld::IdCollection& bodyparts) :mObjects(referenceable), mRaces(races), mClasses(classes), @@ -22,6 +23,7 @@ CSMTools::ReferenceableCheckStage::ReferenceableCheckStage( mScripts(scripts), mModels(models), mIcons(icons), + mBodyParts(bodyparts), mPlayerPresent(false) { mIgnoreBaseRecords = false; @@ -274,11 +276,10 @@ void CSMTools::ReferenceableCheckStage::activatorCheck( const ESM::Activator& activator = (dynamic_cast& >(baseRecord)).get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Activator, activator.mId); - //Checking for model, IIRC all activators should have a model if (activator.mModel.empty()) - messages.push_back (std::make_pair (id, "Model is missing")); + messages.add(id, "Model is missing", "", CSMDoc::Message::Severity_Error); else if (mModels.searchId(activator.mModel) == -1) - messages.push_back (std::make_pair (id, "Model '" + activator.mModel + "' does not exist")); + messages.add(id, "Model '" + activator.mModel + "' does not exist", "", CSMDoc::Message::Severity_Error); // Check that mentioned scripts exist scriptCheck(activator, messages, id.toString()); @@ -299,7 +300,6 @@ void CSMTools::ReferenceableCheckStage::potionCheck( CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Potion, potion.mId); inventoryItemCheck(potion, messages, id.toString()); - //IIRC potion can have empty effects list just fine. // Check that mentioned scripts exist scriptCheck(potion, messages, id.toString()); @@ -344,13 +344,13 @@ void CSMTools::ReferenceableCheckStage::armorCheck( inventoryItemCheck(armor, messages, id.toString(), true); - //checking for armor class, armor should have poistive armor class, but 0 is considered legal + // Armor should have positive armor class, but 0 class is not an error if (armor.mData.mArmor < 0) - messages.push_back (std::make_pair (id, "Armor class is negative")); + messages.add(id, "Armor class is negative", "", CSMDoc::Message::Severity_Error); - //checking for health. Only positive numbers are allowed, or 0 is illegal + // Armor durability must be a positive number if (armor.mData.mHealth <= 0) - messages.push_back (std::make_pair (id, "Durability is non-positive")); + messages.add(id, "Durability is non-positive", "", CSMDoc::Message::Severity_Error); // Check that mentioned scripts exist scriptCheck(armor, messages, id.toString()); @@ -389,19 +389,19 @@ void CSMTools::ReferenceableCheckStage::containerCheck( const ESM::Container& container = (dynamic_cast& >(baseRecord)).get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Container, container.mId); + //checking for name + if (container.mName.empty()) + messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Error); + //Checking for model if (container.mModel.empty()) - messages.push_back (std::make_pair (id, "Model is missing")); + messages.add(id, "Model is missing", "", CSMDoc::Message::Severity_Error); else if (mModels.searchId(container.mModel) == -1) - messages.push_back (std::make_pair (id, "Model '" + container.mModel + "' does not exist")); + messages.add(id, "Model '" + container.mModel + "' does not exist", "", CSMDoc::Message::Severity_Error); //Checking for capacity (weight) if (container.mWeight < 0) //0 is allowed - messages.push_back (std::make_pair (id, "Capacity is negative")); - - //checking for name - if (container.mName.empty()) - messages.push_back (std::make_pair (id, "Name is missing")); + messages.add(id, "Capacity is negative", "", CSMDoc::Message::Severity_Error); //checking contained items inventoryListCheck(container.mInventory.mList, messages, id.toString()); @@ -423,69 +423,81 @@ void CSMTools::ReferenceableCheckStage::creatureCheck ( const ESM::Creature& creature = (dynamic_cast&>(baseRecord)).get(); CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Creature, creature.mId); - if (creature.mModel.empty()) - messages.push_back (std::make_pair (id, "Model is missing")); - else if (mModels.searchId(creature.mModel) == -1) - messages.push_back (std::make_pair (id, "Model '" + creature.mModel + "' does not exist")); - if (creature.mName.empty()) - messages.push_back (std::make_pair (id, "Name is missing")); + messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Error); + + if (creature.mModel.empty()) + messages.add(id, "Model is missing", "", CSMDoc::Message::Severity_Error); + else if (mModels.searchId(creature.mModel) == -1) + messages.add(id, "Model '" + creature.mModel + "' does not exist", "", CSMDoc::Message::Severity_Error); //stats checks - if (creature.mData.mLevel < 1) - messages.push_back (std::make_pair (id, "Level is non-positive")); + if (creature.mData.mLevel <= 0) + messages.add(id, "Level is non-positive", "", CSMDoc::Message::Severity_Warning); if (creature.mData.mStrength < 0) - messages.push_back (std::make_pair (id, "Strength is negative")); - + messages.add(id, "Strength is negative", "", CSMDoc::Message::Severity_Warning); if (creature.mData.mIntelligence < 0) - messages.push_back (std::make_pair (id, "Intelligence is negative")); - + messages.add(id, "Intelligence is negative", "", CSMDoc::Message::Severity_Warning); if (creature.mData.mWillpower < 0) - messages.push_back (std::make_pair (id, "Willpower is negative")); - + messages.add(id, "Willpower is negative", "", CSMDoc::Message::Severity_Warning); if (creature.mData.mAgility < 0) - messages.push_back (std::make_pair (id, "Agility is negative")); - + messages.add(id, "Agility is negative", "", CSMDoc::Message::Severity_Warning); if (creature.mData.mSpeed < 0) - messages.push_back (std::make_pair (id, "Speed is negative")); - + messages.add(id, "Speed is negative", "", CSMDoc::Message::Severity_Warning); if (creature.mData.mEndurance < 0) - messages.push_back (std::make_pair (id, "Endurance is negative")); - + messages.add(id, "Endurance is negative", "", CSMDoc::Message::Severity_Warning); if (creature.mData.mPersonality < 0) - messages.push_back (std::make_pair (id, "Personality is negative")); - + messages.add(id, "Personality is negative", "", CSMDoc::Message::Severity_Warning); if (creature.mData.mLuck < 0) - messages.push_back (std::make_pair (id, "Luck is negative")); + messages.add(id, "Luck is negative", "", CSMDoc::Message::Severity_Warning); + + if (creature.mData.mCombat < 0) + messages.add(id, "Combat is negative", "", CSMDoc::Message::Severity_Warning); + if (creature.mData.mMagic < 0) + messages.add(id, "Magic is negative", "", CSMDoc::Message::Severity_Warning); + if (creature.mData.mStealth < 0) + messages.add(id, "Stealth is negative", "", CSMDoc::Message::Severity_Warning); if (creature.mData.mHealth < 0) - messages.push_back (std::make_pair (id, "Health is negative")); + messages.add(id, "Health is negative", "", CSMDoc::Message::Severity_Error); + if (creature.mData.mMana < 0) + messages.add(id, "Magicka is negative", "", CSMDoc::Message::Severity_Error); + if (creature.mData.mFatigue < 0) + messages.add(id, "Fatigue is negative", "", CSMDoc::Message::Severity_Error); if (creature.mData.mSoul < 0) - messages.push_back (std::make_pair (id, "Soul value is negative")); + messages.add(id, "Soul value is negative", "", CSMDoc::Message::Severity_Error); for (int i = 0; i < 6; ++i) { if (creature.mData.mAttack[i] < 0) - { - messages.push_back (std::make_pair (id, "One of attacks has negative damage")); - break; - } + messages.add(id, "Attack " + std::to_string(i/2 + 1) + " has negative" + (i % 2 == 0 ? " minimum " : " maximum ") + "damage", "", CSMDoc::Message::Severity_Error); + if (i % 2 == 0 && creature.mData.mAttack[i] > creature.mData.mAttack[i+1]) + messages.add(id, "Attack " + std::to_string(i/2 + 1) + " has minimum damage higher than maximum damage", "", CSMDoc::Message::Severity_Warning); } - //TODO, find meaning of other values if (creature.mData.mGold < 0) - messages.push_back (std::make_pair (id, "Gold count is negative")); + messages.add(id, "Gold count is negative", "", CSMDoc::Message::Severity_Error); if (creature.mScale == 0) - messages.push_back (std::make_pair (id, "Scale is equal to zero")); + messages.add(id, "Scale is equal to zero", "", CSMDoc::Message::Severity_Error); + + if (!creature.mOriginal.empty()) + { + CSMWorld::RefIdData::LocalIndex index = mObjects.searchId(creature.mOriginal); + if (index.first == -1) + messages.add(id, "Parent creature '" + creature.mOriginal + "' does not exist", "", CSMDoc::Message::Severity_Error); + else if (index.second != CSMWorld::UniversalId::Type_Creature) + messages.add(id, "'" + creature.mOriginal + "' is not a creature", "", CSMDoc::Message::Severity_Error); + } // Check inventory inventoryListCheck(creature.mInventory.mList, messages, id.toString()); // Check that mentioned scripts exist scriptCheck(creature, messages, id.toString()); + /// \todo Check spells, teleport table, AI data and AI packages for validity } void CSMTools::ReferenceableCheckStage::doorCheck( @@ -503,12 +515,12 @@ void CSMTools::ReferenceableCheckStage::doorCheck( //usual, name or model if (door.mName.empty()) - messages.push_back (std::make_pair (id, "Name is missing")); + messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Error); if (door.mModel.empty()) - messages.push_back (std::make_pair (id, "Model is missing")); + messages.add(id, "Model is missing", "", CSMDoc::Message::Severity_Error); else if (mModels.searchId(door.mModel) == -1) - messages.push_back (std::make_pair (id, "Model '" + door.mModel + "' does not exist")); + messages.add(id, "Model '" + door.mModel + "' does not exist", "", CSMDoc::Message::Severity_Error); // Check that mentioned scripts exist scriptCheck(door, messages, id.toString()); @@ -582,7 +594,7 @@ void CSMTools::ReferenceableCheckStage::lightCheck( CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Light, light.mId); if (light.mData.mRadius < 0) - messages.push_back (std::make_pair (id, "Light radius is negative")); + messages.add(id, "Light radius is negative", "", CSMDoc::Message::Severity_Error); if (light.mData.mFlags & ESM::Light::Carry) inventoryItemCheck(light, messages, id.toString()); @@ -657,89 +669,87 @@ void CSMTools::ReferenceableCheckStage::npcCheck ( char disposition(npc.mNpdt.mDisposition); char reputation(npc.mNpdt.mReputation); char rank(npc.mNpdt.mRank); - //Don't know what unknown is for int gold(npc.mNpdt.mGold); if (npc.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS) //12 = autocalculated { if ((npc.mFlags & ESM::NPC::Autocalc) == 0) //0x0010 = autocalculated flag { - messages.push_back (std::make_pair (id, npc.mId + " mNpdtType or flags mismatch!")); //should not happen? + messages.add(id, "NPC with autocalculated stats doesn't have autocalc flag turned on", "", CSMDoc::Message::Severity_Error); //should not happen? return; } - - level = npc.mNpdt.mLevel; - disposition = npc.mNpdt.mDisposition; - reputation = npc.mNpdt.mReputation; - rank = npc.mNpdt.mRank; - gold = npc.mNpdt.mGold; } else { - if (npc.mNpdt.mAgility == 0) - messages.push_back (std::make_pair (id, "Agility is equal to zero")); - - if (npc.mNpdt.mEndurance == 0) - messages.push_back (std::make_pair (id, "Endurance is equal to zero")); - - if (npc.mNpdt.mIntelligence == 0) - messages.push_back (std::make_pair (id, "Intelligence is equal to zero")); - - if (npc.mNpdt.mLuck == 0) - messages.push_back (std::make_pair (id, "Luck is equal to zero")); - - if (npc.mNpdt.mPersonality == 0) - messages.push_back (std::make_pair (id, "Personality is equal to zero")); - if (npc.mNpdt.mStrength == 0) - messages.push_back (std::make_pair (id, "Strength is equal to zero")); - - if (npc.mNpdt.mSpeed == 0) - messages.push_back (std::make_pair (id, "Speed is equal to zero")); - + messages.add(id, "Strength is equal to zero", "", CSMDoc::Message::Severity_Warning); + if (npc.mNpdt.mIntelligence == 0) + messages.add(id, "Intelligence is equal to zero", "", CSMDoc::Message::Severity_Warning); if (npc.mNpdt.mWillpower == 0) - messages.push_back (std::make_pair (id, "Willpower is equal to zero")); + messages.add(id, "Willpower is equal to zero", "", CSMDoc::Message::Severity_Warning); + if (npc.mNpdt.mAgility == 0) + messages.add(id, "Agility is equal to zero", "", CSMDoc::Message::Severity_Warning); + if (npc.mNpdt.mSpeed == 0) + messages.add(id, "Speed is equal to zero", "", CSMDoc::Message::Severity_Warning); + if (npc.mNpdt.mEndurance == 0) + messages.add(id, "Endurance is equal to zero", "", CSMDoc::Message::Severity_Warning); + if (npc.mNpdt.mPersonality == 0) + messages.add(id, "Personality is equal to zero", "", CSMDoc::Message::Severity_Warning); + if (npc.mNpdt.mLuck == 0) + messages.add(id, "Luck is equal to zero", "", CSMDoc::Message::Severity_Warning); } if (level <= 0) - messages.push_back (std::make_pair (id, "Level is non-positive")); + messages.add(id, "Level is non-positive", "", CSMDoc::Message::Severity_Warning); if (gold < 0) - messages.push_back (std::make_pair (id, "Gold count is negative")); + messages.add(id, "Gold count is negative", "", CSMDoc::Message::Severity_Error); if (npc.mName.empty()) - messages.push_back (std::make_pair (id, "Name is missing")); + messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Error); if (npc.mClass.empty()) - messages.push_back (std::make_pair (id, "Class is missing")); + messages.add(id, "Class is missing", "", CSMDoc::Message::Severity_Error); else if (mClasses.searchId (npc.mClass) == -1) - messages.push_back (std::make_pair (id, "Class '" + npc.mClass + "' does not exist")); + messages.add(id, "Class '" + npc.mClass + "' does not exist", "", CSMDoc::Message::Severity_Error); if (npc.mRace.empty()) - messages.push_back (std::make_pair (id, "Race is missing")); + messages.add(id, "Race is missing", "", CSMDoc::Message::Severity_Error); else if (mRaces.searchId (npc.mRace) == -1) - messages.push_back (std::make_pair (id, "Race '" + npc.mRace + "' does not exist")); + messages.add(id, "Race '" + npc.mRace + "' does not exist", "", CSMDoc::Message::Severity_Error); if (disposition < 0) - messages.push_back (std::make_pair (id, "Disposition is negative")); + messages.add(id, "Disposition is negative", "", CSMDoc::Message::Severity_Warning); if (reputation < 0) - messages.push_back (std::make_pair (id, "Reputation is negative")); + messages.add(id, "Reputation is negative", "", CSMDoc::Message::Severity_Warning); if (!npc.mFaction.empty()) { if (rank < 0) - messages.push_back (std::make_pair (id, "Faction rank is negative")); + messages.add(id, "Faction rank is negative", "", CSMDoc::Message::Severity_Warning); if (mFactions.searchId(npc.mFaction) == -1) - messages.push_back (std::make_pair (id, "Faction '" + npc.mFaction + "' does not exist")); + messages.add(id, "Faction '" + npc.mFaction + "' does not exist", "", CSMDoc::Message::Severity_Error); } if (npc.mHead.empty()) - messages.push_back (std::make_pair (id, "Head is missing")); // ADD CHECK HERE + messages.push_back (std::make_pair (id, "Head is missing")); + else + { + if (mBodyParts.searchId(npc.mHead) == -1) + messages.add(id, "Head body part '" + npc.mHead + "' does not exist", "", CSMDoc::Message::Severity_Error); + /// \todo Check gender, race and other body parts stuff validity for the specific NPC + } if (npc.mHair.empty()) - messages.push_back (std::make_pair (id, "Hair is missing")); // ADD CHECK HERE + messages.push_back (std::make_pair (id, "Hair is missing")); + else + { + if (mBodyParts.searchId(npc.mHair) == -1) + messages.add(id, "Hair body part '" + npc.mHair + "' does not exist", "", CSMDoc::Message::Severity_Error); + /// \todo Check gender, race and other body part stuff validity for the specific NPC + } // Check inventory inventoryListCheck(npc.mInventory.mList, messages, id.toString()); @@ -799,14 +809,14 @@ void CSMTools::ReferenceableCheckStage::weaponCheck( weapon.mData.mType == ESM::Weapon::Bolt)) { if (weapon.mData.mSlash[0] > weapon.mData.mSlash[1]) - messages.push_back (std::make_pair (id, "Minimum slash damage higher than maximum")); + messages.add(id, "Minimum slash damage higher than maximum", "", CSMDoc::Message::Severity_Warning); if (weapon.mData.mThrust[0] > weapon.mData.mThrust[1]) - messages.push_back (std::make_pair (id, "Minimum thrust damage is higher than maximum")); + messages.add(id, "Minimum thrust damage higher than maximum", "", CSMDoc::Message::Severity_Warning); } if (weapon.mData.mChop[0] > weapon.mData.mChop[1]) - messages.push_back (std::make_pair (id, "Minimum chop damage is higher than maximum")); + messages.add(id, "Minimum chop damage higher than maximum", "", CSMDoc::Message::Severity_Warning); if (!(weapon.mData.mType == ESM::Weapon::Arrow || weapon.mData.mType == ESM::Weapon::Bolt || @@ -814,10 +824,10 @@ void CSMTools::ReferenceableCheckStage::weaponCheck( { //checking of health if (weapon.mData.mHealth == 0) - messages.push_back (std::make_pair (id, "Durability is equal to zero")); + messages.add(id, "Durability is equal to zero", "", CSMDoc::Message::Severity_Warning); if (weapon.mData.mReach < 0) - messages.push_back (std::make_pair (id, "Reach is negative")); + messages.add(id, "Reach is negative", "", CSMDoc::Message::Severity_Error); } } @@ -880,9 +890,9 @@ void CSMTools::ReferenceableCheckStage::staticCheck ( CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Static, staticElement.mId); if (staticElement.mModel.empty()) - messages.push_back (std::make_pair (id, "Model is missing")); + messages.add(id, "Model is missing", "", CSMDoc::Message::Severity_Error); else if (mModels.searchId(staticElement.mModel) == -1) - messages.push_back (std::make_pair (id, "Model '" + staticElement.mModel + "' does not exist")); + messages.add(id, "Model '" + staticElement.mModel + "' does not exist", "", CSMDoc::Message::Severity_Error); } //final check @@ -890,8 +900,7 @@ void CSMTools::ReferenceableCheckStage::staticCheck ( void CSMTools::ReferenceableCheckStage::finalCheck (CSMDoc::Messages& messages) { if (!mPlayerPresent) - messages.push_back (std::make_pair (CSMWorld::UniversalId::Type_Referenceables, - "Player record is missing")); + messages.add(CSMWorld::UniversalId::Type_Referenceables, "Player record is missing", "", CSMDoc::Message::Severity_SeriousError); } void CSMTools::ReferenceableCheckStage::inventoryListCheck( @@ -905,7 +914,7 @@ void CSMTools::ReferenceableCheckStage::inventoryListCheck( CSMWorld::RefIdData::LocalIndex localIndex = mObjects.searchId(itemName); if (localIndex.first == -1) - messages.push_back (std::make_pair (id, "Item '" + itemName + "' does not exist")); + messages.add(id, "Item '" + itemName + "' does not exist", "", CSMDoc::Message::Severity_Error); else { // Needs to accommodate containers, creatures, and NPCs @@ -926,7 +935,7 @@ void CSMTools::ReferenceableCheckStage::inventoryListCheck( case CSMWorld::UniversalId::Type_ItemLevelledList: break; default: - messages.push_back (std::make_pair(id, "'" + itemName + "' is not an item")); + messages.add(id, "'" + itemName + "' is not an item", "", CSMDoc::Message::Severity_Error); } } } @@ -938,64 +947,64 @@ template void CSMTools::ReferenceableCheckStage::inventoryItemChe const Item& someItem, CSMDoc::Messages& messages, const std::string& someID, bool enchantable) { if (someItem.mName.empty()) - messages.push_back (std::make_pair (someID, "Name is missing")); + messages.add(someID, "Name is missing", "", CSMDoc::Message::Severity_Error); //Checking for weight if (someItem.mData.mWeight < 0) - messages.push_back (std::make_pair (someID, "Weight is negative")); + messages.add(someID, "Weight is negative", "", CSMDoc::Message::Severity_Error); //Checking for value if (someItem.mData.mValue < 0) - messages.push_back (std::make_pair (someID, "Value is negative")); + messages.add(someID, "Value is negative", "", CSMDoc::Message::Severity_Error); //checking for model if (someItem.mModel.empty()) - messages.push_back (std::make_pair (someID, "Model is missing")); + messages.add(someID, "Model is missing", "", CSMDoc::Message::Severity_Error); else if (mModels.searchId(someItem.mModel) == -1) - messages.push_back(std::make_pair(someID, "Model '" + someItem.mModel + "' does not exist")); + messages.add(someID, "Model '" + someItem.mModel + "' does not exist", "", CSMDoc::Message::Severity_Error); //checking for icon if (someItem.mIcon.empty()) - messages.push_back (std::make_pair (someID, "Icon is missing")); + messages.add(someID, "Icon is missing", "", CSMDoc::Message::Severity_Error); else if (mIcons.searchId(someItem.mIcon) == -1) { std::string ddsIcon = someItem.mIcon; if (!(Misc::ResourceHelpers::changeExtensionToDds(ddsIcon) && mIcons.searchId(ddsIcon) != -1)) - messages.push_back(std::make_pair(someID, "Icon '" + someItem.mIcon + "' does not exist")); + messages.add(someID, "Icon '" + someItem.mIcon + "' does not exist", "", CSMDoc::Message::Severity_Error); } if (enchantable && someItem.mData.mEnchant < 0) - messages.push_back (std::make_pair (someID, "Enchantment points number is negative")); + messages.add(someID, "Enchantment points number is negative", "", CSMDoc::Message::Severity_Error); } template void CSMTools::ReferenceableCheckStage::inventoryItemCheck ( const Item& someItem, CSMDoc::Messages& messages, const std::string& someID) { if (someItem.mName.empty()) - messages.push_back (std::make_pair (someID, "Name is missing")); + messages.add(someID, "Name is missing", "", CSMDoc::Message::Severity_Error); //Checking for weight if (someItem.mData.mWeight < 0) - messages.push_back (std::make_pair (someID, "Weight is negative")); + messages.add(someID, "Weight is negative", "", CSMDoc::Message::Severity_Error); //Checking for value if (someItem.mData.mValue < 0) - messages.push_back (std::make_pair (someID, "Value is negative")); + messages.add(someID, "Value is negative", "", CSMDoc::Message::Severity_Error); //checking for model if (someItem.mModel.empty()) - messages.push_back (std::make_pair (someID, "Model is missing")); + messages.add(someID, "Model is missing", "", CSMDoc::Message::Severity_Error); else if (mModels.searchId(someItem.mModel) == -1) - messages.push_back (std::make_pair (someID, "Model '" + someItem.mModel + "' does not exist")); + messages.add(someID, "Model '" + someItem.mModel + "' does not exist", "", CSMDoc::Message::Severity_Error); //checking for icon if (someItem.mIcon.empty()) - messages.push_back (std::make_pair (someID, "Icon is missing")); + messages.add(someID, "Icon is missing", "", CSMDoc::Message::Severity_Error); else if (mIcons.searchId(someItem.mIcon) == -1) { std::string ddsIcon = someItem.mIcon; if (!(Misc::ResourceHelpers::changeExtensionToDds(ddsIcon) && mIcons.searchId(ddsIcon) != -1)) - messages.push_back(std::make_pair(someID, "Icon '" + someItem.mIcon + "' does not exist")); + messages.add(someID, "Icon '" + someItem.mIcon + "' does not exist", "", CSMDoc::Message::Severity_Error); } } @@ -1003,17 +1012,17 @@ template void CSMTools::ReferenceableCheckStage::toolCheck ( const Tool& someTool, CSMDoc::Messages& messages, const std::string& someID, bool canBeBroken) { if (someTool.mData.mQuality <= 0) - messages.push_back (std::make_pair (someID, "Quality is non-positive")); + messages.add(someID, "Quality is non-positive", "", CSMDoc::Message::Severity_Error); if (canBeBroken && someTool.mData.mUses<=0) - messages.push_back (std::make_pair (someID, "Number of uses is non-positive")); + messages.add(someID, "Number of uses is non-positive", "", CSMDoc::Message::Severity_Error); } template void CSMTools::ReferenceableCheckStage::toolCheck ( const Tool& someTool, CSMDoc::Messages& messages, const std::string& someID) -{ +{ if (someTool.mData.mQuality <= 0) - messages.push_back (std::make_pair (someID, "Quality is non-positive")); + messages.add(someID, "Quality is non-positive", "", CSMDoc::Message::Severity_Error); } template void CSMTools::ReferenceableCheckStage::listCheck ( @@ -1022,10 +1031,10 @@ template void CSMTools::ReferenceableCheckStage::listCheck ( for (unsigned i = 0; i < someList.mList.size(); ++i) { if (mObjects.searchId(someList.mList[i].mId).first == -1) - messages.push_back (std::make_pair (someID, "Object '" + someList.mList[i].mId + "' does not exist")); + messages.add(someID, "Object '" + someList.mList[i].mId + "' does not exist", "", CSMDoc::Message::Severity_Error); if (someList.mList[i].mLevel < 1) - messages.push_back (std::make_pair (someID, "Level of item '" + someList.mList[i].mId + "' is non-positive")); + messages.add(someID, "Level of item '" + someList.mList[i].mId + "' is non-positive", "", CSMDoc::Message::Severity_Error); } } @@ -1035,6 +1044,6 @@ template void CSMTools::ReferenceableCheckStage::scriptCheck ( if (!someTool.mScript.empty()) { if (mScripts.searchId(someTool.mScript) == -1) - messages.push_back (std::make_pair (someID, "Script '"+someTool.mScript+"' does not exist")); + messages.add(someID, "Script '" + someTool.mScript + "' does not exist", "", CSMDoc::Message::Severity_Error); } } diff --git a/apps/opencs/model/tools/referenceablecheck.hpp b/apps/opencs/model/tools/referenceablecheck.hpp index 5da737875..d8682c907 100644 --- a/apps/opencs/model/tools/referenceablecheck.hpp +++ b/apps/opencs/model/tools/referenceablecheck.hpp @@ -19,7 +19,8 @@ namespace CSMTools const CSMWorld::IdCollection& factions, const CSMWorld::IdCollection& scripts, const CSMWorld::Resources& models, - const CSMWorld::Resources& icons); + const CSMWorld::Resources& icons, + const CSMWorld::IdCollection& bodyparts); virtual void perform(int stage, CSMDoc::Messages& messages); virtual int setup(); @@ -86,6 +87,7 @@ namespace CSMTools const CSMWorld::IdCollection& mScripts; const CSMWorld::Resources& mModels; const CSMWorld::Resources& mIcons; + const CSMWorld::IdCollection& mBodyParts; bool mPlayerPresent; bool mIgnoreBaseRecords; }; diff --git a/apps/opencs/model/tools/tools.cpp b/apps/opencs/model/tools/tools.cpp index 87b5041e0..b9a0abb75 100644 --- a/apps/opencs/model/tools/tools.cpp +++ b/apps/opencs/model/tools/tools.cpp @@ -83,7 +83,8 @@ CSMDoc::OperationHolder *CSMTools::Tools::getVerifier() mVerifierOperation->appendStage (new SpellCheckStage (mData.getSpells())); mVerifierOperation->appendStage (new ReferenceableCheckStage (mData.getReferenceables().getDataSet(), mData.getRaces(), mData.getClasses(), mData.getFactions(), mData.getScripts(), - mData.getResources (CSMWorld::UniversalId::Type_Meshes), mData.getResources (CSMWorld::UniversalId::Type_Icons))); + mData.getResources (CSMWorld::UniversalId::Type_Meshes), mData.getResources (CSMWorld::UniversalId::Type_Icons), + mData.getBodyParts())); mVerifierOperation->appendStage (new ReferenceCheckStage(mData.getReferences(), mData.getReferenceables(), mData.getCells(), mData.getFactions())); From 34ffaa2fe2cf6f85094289769cc788f55a9c4c03 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 26 Aug 2018 21:18:17 +0300 Subject: [PATCH 032/163] Make finishing touches to object record verifiers --- .../opencs/model/tools/referenceablecheck.cpp | 95 ++++++++++--------- .../opencs/model/tools/referenceablecheck.hpp | 2 +- 2 files changed, 49 insertions(+), 48 deletions(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index c61d84d51..60d4a6b58 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -16,7 +16,7 @@ CSMTools::ReferenceableCheckStage::ReferenceableCheckStage( const CSMWorld::Resources& models, const CSMWorld::Resources& icons, const CSMWorld::IdCollection& bodyparts) - :mObjects(referenceable), + :mReferencables(referenceable), mRaces(races), mClasses(classes), mFactions(faction), @@ -32,201 +32,201 @@ CSMTools::ReferenceableCheckStage::ReferenceableCheckStage( void CSMTools::ReferenceableCheckStage::perform (int stage, CSMDoc::Messages& messages) { //Checks for books, than, when stage is above mBooksSize goes to other checks, with (stage - PrevSum) as stage. - const int bookSize(mObjects.getBooks().getSize()); + const int bookSize(mReferencables.getBooks().getSize()); if (stage < bookSize) { - bookCheck(stage, mObjects.getBooks(), messages); + bookCheck(stage, mReferencables.getBooks(), messages); return; } stage -= bookSize; - const int activatorSize(mObjects.getActivators().getSize()); + const int activatorSize(mReferencables.getActivators().getSize()); if (stage < activatorSize) { - activatorCheck(stage, mObjects.getActivators(), messages); + activatorCheck(stage, mReferencables.getActivators(), messages); return; } stage -= activatorSize; - const int potionSize(mObjects.getPotions().getSize()); + const int potionSize(mReferencables.getPotions().getSize()); if (stage < potionSize) { - potionCheck(stage, mObjects.getPotions(), messages); + potionCheck(stage, mReferencables.getPotions(), messages); return; } stage -= potionSize; - const int apparatusSize(mObjects.getApparati().getSize()); + const int apparatusSize(mReferencables.getApparati().getSize()); if (stage < apparatusSize) { - apparatusCheck(stage, mObjects.getApparati(), messages); + apparatusCheck(stage, mReferencables.getApparati(), messages); return; } stage -= apparatusSize; - const int armorSize(mObjects.getArmors().getSize()); + const int armorSize(mReferencables.getArmors().getSize()); if (stage < armorSize) { - armorCheck(stage, mObjects.getArmors(), messages); + armorCheck(stage, mReferencables.getArmors(), messages); return; } stage -= armorSize; - const int clothingSize(mObjects.getClothing().getSize()); + const int clothingSize(mReferencables.getClothing().getSize()); if (stage < clothingSize) { - clothingCheck(stage, mObjects.getClothing(), messages); + clothingCheck(stage, mReferencables.getClothing(), messages); return; } stage -= clothingSize; - const int containerSize(mObjects.getContainers().getSize()); + const int containerSize(mReferencables.getContainers().getSize()); if (stage < containerSize) { - containerCheck(stage, mObjects.getContainers(), messages); + containerCheck(stage, mReferencables.getContainers(), messages); return; } stage -= containerSize; - const int doorSize(mObjects.getDoors().getSize()); + const int doorSize(mReferencables.getDoors().getSize()); if (stage < doorSize) { - doorCheck(stage, mObjects.getDoors(), messages); + doorCheck(stage, mReferencables.getDoors(), messages); return; } stage -= doorSize; - const int ingredientSize(mObjects.getIngredients().getSize()); + const int ingredientSize(mReferencables.getIngredients().getSize()); if (stage < ingredientSize) { - ingredientCheck(stage, mObjects.getIngredients(), messages); + ingredientCheck(stage, mReferencables.getIngredients(), messages); return; } stage -= ingredientSize; - const int creatureLevListSize(mObjects.getCreatureLevelledLists().getSize()); + const int creatureLevListSize(mReferencables.getCreatureLevelledLists().getSize()); if (stage < creatureLevListSize) { - creaturesLevListCheck(stage, mObjects.getCreatureLevelledLists(), messages); + creaturesLevListCheck(stage, mReferencables.getCreatureLevelledLists(), messages); return; } stage -= creatureLevListSize; - const int itemLevelledListSize(mObjects.getItemLevelledList().getSize()); + const int itemLevelledListSize(mReferencables.getItemLevelledList().getSize()); if (stage < itemLevelledListSize) { - itemLevelledListCheck(stage, mObjects.getItemLevelledList(), messages); + itemLevelledListCheck(stage, mReferencables.getItemLevelledList(), messages); return; } stage -= itemLevelledListSize; - const int lightSize(mObjects.getLights().getSize()); + const int lightSize(mReferencables.getLights().getSize()); if (stage < lightSize) { - lightCheck(stage, mObjects.getLights(), messages); + lightCheck(stage, mReferencables.getLights(), messages); return; } stage -= lightSize; - const int lockpickSize(mObjects.getLocpicks().getSize()); + const int lockpickSize(mReferencables.getLocpicks().getSize()); if (stage < lockpickSize) { - lockpickCheck(stage, mObjects.getLocpicks(), messages); + lockpickCheck(stage, mReferencables.getLocpicks(), messages); return; } stage -= lockpickSize; - const int miscSize(mObjects.getMiscellaneous().getSize()); + const int miscSize(mReferencables.getMiscellaneous().getSize()); if (stage < miscSize) { - miscCheck(stage, mObjects.getMiscellaneous(), messages); + miscCheck(stage, mReferencables.getMiscellaneous(), messages); return; } stage -= miscSize; - const int npcSize(mObjects.getNPCs().getSize()); + const int npcSize(mReferencables.getNPCs().getSize()); if (stage < npcSize) { - npcCheck(stage, mObjects.getNPCs(), messages); + npcCheck(stage, mReferencables.getNPCs(), messages); return; } stage -= npcSize; - const int weaponSize(mObjects.getWeapons().getSize()); + const int weaponSize(mReferencables.getWeapons().getSize()); if (stage < weaponSize) { - weaponCheck(stage, mObjects.getWeapons(), messages); + weaponCheck(stage, mReferencables.getWeapons(), messages); return; } stage -= weaponSize; - const int probeSize(mObjects.getProbes().getSize()); + const int probeSize(mReferencables.getProbes().getSize()); if (stage < probeSize) { - probeCheck(stage, mObjects.getProbes(), messages); + probeCheck(stage, mReferencables.getProbes(), messages); return; } stage -= probeSize; - const int repairSize(mObjects.getRepairs().getSize()); + const int repairSize(mReferencables.getRepairs().getSize()); if (stage < repairSize) { - repairCheck(stage, mObjects.getRepairs(), messages); + repairCheck(stage, mReferencables.getRepairs(), messages); return; } stage -= repairSize; - const int staticSize(mObjects.getStatics().getSize()); + const int staticSize(mReferencables.getStatics().getSize()); if (stage < staticSize) { - staticCheck(stage, mObjects.getStatics(), messages); + staticCheck(stage, mReferencables.getStatics(), messages); return; } stage -= staticSize; - const int creatureSize(mObjects.getCreatures().getSize()); + const int creatureSize(mReferencables.getCreatures().getSize()); if (stage < creatureSize) { - creatureCheck(stage, mObjects.getCreatures(), messages); + creatureCheck(stage, mReferencables.getCreatures(), messages); return; } // if we come that far, we are about to perform our last, final check. @@ -239,7 +239,7 @@ int CSMTools::ReferenceableCheckStage::setup() mPlayerPresent = false; mIgnoreBaseRecords = CSMPrefs::get()["Reports"]["ignore-base-records"].isTrue(); - return mObjects.getSize() + 1; + return mReferencables.getSize() + 1; } void CSMTools::ReferenceableCheckStage::bookCheck( @@ -300,6 +300,7 @@ void CSMTools::ReferenceableCheckStage::potionCheck( CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Potion, potion.mId); inventoryItemCheck(potion, messages, id.toString()); + /// \todo Check magic effects for validity // Check that mentioned scripts exist scriptCheck(potion, messages, id.toString()); @@ -485,7 +486,7 @@ void CSMTools::ReferenceableCheckStage::creatureCheck ( if (!creature.mOriginal.empty()) { - CSMWorld::RefIdData::LocalIndex index = mObjects.searchId(creature.mOriginal); + CSMWorld::RefIdData::LocalIndex index = mReferencables.searchId(creature.mOriginal); if (index.first == -1) messages.add(id, "Parent creature '" + creature.mOriginal + "' does not exist", "", CSMDoc::Message::Severity_Error); else if (index.second != CSMWorld::UniversalId::Type_Creature) @@ -892,7 +893,7 @@ void CSMTools::ReferenceableCheckStage::staticCheck ( if (staticElement.mModel.empty()) messages.add(id, "Model is missing", "", CSMDoc::Message::Severity_Error); else if (mModels.searchId(staticElement.mModel) == -1) - messages.add(id, "Model '" + staticElement.mModel + "' does not exist", "", CSMDoc::Message::Severity_Error); + messages.add(id, "Model '" + staticElement.mModel + "' does not exist", "", CSMDoc::Message::Severity_Error); } //final check @@ -911,7 +912,7 @@ void CSMTools::ReferenceableCheckStage::inventoryListCheck( for (size_t i = 0; i < itemList.size(); ++i) { std::string itemName = itemList[i].mItem.toString(); - CSMWorld::RefIdData::LocalIndex localIndex = mObjects.searchId(itemName); + CSMWorld::RefIdData::LocalIndex localIndex = mReferencables.searchId(itemName); if (localIndex.first == -1) messages.add(id, "Item '" + itemName + "' does not exist", "", CSMDoc::Message::Severity_Error); @@ -1020,7 +1021,7 @@ template void CSMTools::ReferenceableCheckStage::toolCheck ( template void CSMTools::ReferenceableCheckStage::toolCheck ( const Tool& someTool, CSMDoc::Messages& messages, const std::string& someID) -{ +{ if (someTool.mData.mQuality <= 0) messages.add(someID, "Quality is non-positive", "", CSMDoc::Message::Severity_Error); } @@ -1030,7 +1031,7 @@ template void CSMTools::ReferenceableCheckStage::listCheck ( { for (unsigned i = 0; i < someList.mList.size(); ++i) { - if (mObjects.searchId(someList.mList[i].mId).first == -1) + if (mReferencables.searchId(someList.mList[i].mId).first == -1) messages.add(someID, "Object '" + someList.mList[i].mId + "' does not exist", "", CSMDoc::Message::Severity_Error); if (someList.mList[i].mLevel < 1) diff --git a/apps/opencs/model/tools/referenceablecheck.hpp b/apps/opencs/model/tools/referenceablecheck.hpp index d8682c907..e55e5fad9 100644 --- a/apps/opencs/model/tools/referenceablecheck.hpp +++ b/apps/opencs/model/tools/referenceablecheck.hpp @@ -80,7 +80,7 @@ namespace CSMTools CSMDoc::Messages& messages, const std::string& someID); - const CSMWorld::RefIdData& mObjects; + const CSMWorld::RefIdData& mReferencables; const CSMWorld::IdCollection& mRaces; const CSMWorld::IdCollection& mClasses; const CSMWorld::IdCollection& mFactions; From 0bdb7ea92f77c695f90e10b0eda0d581561515cb Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 26 Aug 2018 21:40:07 +0300 Subject: [PATCH 033/163] Cleanup --- apps/opencs/model/tools/birthsigncheck.cpp | 26 ++----- apps/opencs/model/tools/birthsigncheck.hpp | 3 - apps/opencs/model/tools/classcheck.cpp | 4 -- apps/opencs/model/tools/magiceffectcheck.cpp | 70 ++++++------------- .../opencs/model/tools/referenceablecheck.cpp | 2 + 5 files changed, 28 insertions(+), 77 deletions(-) diff --git a/apps/opencs/model/tools/birthsigncheck.cpp b/apps/opencs/model/tools/birthsigncheck.cpp index c582da8e4..a0ae4b11f 100644 --- a/apps/opencs/model/tools/birthsigncheck.cpp +++ b/apps/opencs/model/tools/birthsigncheck.cpp @@ -7,17 +7,6 @@ #include "../world/universalid.hpp" - -std::string CSMTools::BirthsignCheckStage::checkTexture(const std::string &texture) const -{ - if (mTextures.searchId(texture) != -1) return std::string(); - - std::string ddsTexture = texture; - if (Misc::ResourceHelpers::changeExtensionToDds(ddsTexture) && mTextures.searchId(ddsTexture) != -1) return std::string(); - - return "Image '" + texture + "' does not exist"; -} - CSMTools::BirthsignCheckStage::BirthsignCheckStage (const CSMWorld::IdCollection& birthsigns, const CSMWorld::Resources &textures) : mBirthsigns(birthsigns), @@ -46,24 +35,21 @@ void CSMTools::BirthsignCheckStage::perform (int stage, CSMDoc::Messages& messag CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Birthsign, birthsign.mId); if (birthsign.mName.empty()) - { messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Error); - } if (birthsign.mDescription.empty()) - { messages.add(id, "Description is missing", "", CSMDoc::Message::Severity_Warning); - } if (birthsign.mTexture.empty()) - { messages.add(id, "Image is missing", "", CSMDoc::Message::Severity_Error); - } else { - const std::string error = checkTexture(birthsign.mTexture); - if (!error.empty()) - messages.add(id, error, "", CSMDoc::Message::Severity_Error); + if (mTextures.searchId(birthsign.mTexture) != -1) + return; + + std::string ddsTexture = birthsign.mTexture; + if (!(Misc::ResourceHelpers::changeExtensionToDds(ddsTexture) && mTextures.searchId(ddsTexture) != -1)) + messages.add(id, "Image '" + birthsign.mTexture + "' does not exist", "", CSMDoc::Message::Severity_Error); } /// \todo check data members that can't be edited in the table view diff --git a/apps/opencs/model/tools/birthsigncheck.hpp b/apps/opencs/model/tools/birthsigncheck.hpp index d032d3cef..9001c524c 100644 --- a/apps/opencs/model/tools/birthsigncheck.hpp +++ b/apps/opencs/model/tools/birthsigncheck.hpp @@ -17,9 +17,6 @@ namespace CSMTools const CSMWorld::Resources &mTextures; bool mIgnoreBaseRecords; - private: - std::string checkTexture(const std::string &texture) const; - public: BirthsignCheckStage (const CSMWorld::IdCollection &birthsigns, diff --git a/apps/opencs/model/tools/classcheck.cpp b/apps/opencs/model/tools/classcheck.cpp index 9984c3287..25cca8fcd 100644 --- a/apps/opencs/model/tools/classcheck.cpp +++ b/apps/opencs/model/tools/classcheck.cpp @@ -49,12 +49,10 @@ void CSMTools::ClassCheckStage::perform (int stage, CSMDoc::Messages& messages) // test for invalid attributes for (int i=0; i<2; ++i) - { if (class_.mData.mAttribute[i]==-1) { messages.add(id, "Attribute #" + std::to_string(i) + " is not set", "", CSMDoc::Message::Severity_Error); } - } if (class_.mData.mAttribute[0]==class_.mData.mAttribute[1] && class_.mData.mAttribute[0]!=-1) { @@ -69,10 +67,8 @@ void CSMTools::ClassCheckStage::perform (int stage, CSMDoc::Messages& messages) ++skills[class_.mData.mSkills[i][i2]]; for (auto &skill : skills) - { if (skill.second>1) { messages.add(id, "Skill " + ESM::Skill::indexToId (skill.first) + " is listed more than once", "", CSMDoc::Message::Severity_Warning); } - } } diff --git a/apps/opencs/model/tools/magiceffectcheck.cpp b/apps/opencs/model/tools/magiceffectcheck.cpp index 943d89cdc..b62e791a4 100644 --- a/apps/opencs/model/tools/magiceffectcheck.cpp +++ b/apps/opencs/model/tools/magiceffectcheck.cpp @@ -4,17 +4,6 @@ #include "../prefs/state.hpp" -std::string CSMTools::MagicEffectCheckStage::checkTexture(const std::string &texture, bool isIcon) const -{ - const CSMWorld::Resources &textures = isIcon ? mIcons : mTextures; - if (textures.searchId(texture) != -1) return std::string(); - - std::string ddsTexture = texture; - if (Misc::ResourceHelpers::changeExtensionToDds(ddsTexture) && textures.searchId(ddsTexture) != -1) return std::string(); - - return (isIcon ? "Icon '" : "Particle '") + texture + "' does not exist"; -} - std::string CSMTools::MagicEffectCheckStage::checkObject(const std::string &id, const CSMWorld::UniversalId &type, const std::string &column) const @@ -25,12 +14,6 @@ std::string CSMTools::MagicEffectCheckStage::checkObject(const std::string &id, return std::string(); } -std::string CSMTools::MagicEffectCheckStage::checkSound(const std::string &id, const std::string &column) const -{ - if (!id.empty() && mSounds.searchId(id) == -1) return (column + " '" + id + "' " + "does not exist"); - return std::string(); -} - CSMTools::MagicEffectCheckStage::MagicEffectCheckStage(const CSMWorld::IdCollection &effects, const CSMWorld::IdCollection &sounds, const CSMWorld::RefIdCollection &objects, @@ -79,16 +62,22 @@ void CSMTools::MagicEffectCheckStage::perform(int stage, CSMDoc::Messages &messa } else { - const std::string error = checkTexture(effect.mIcon, true); - if (!error.empty()) - messages.add(id, error, "", CSMDoc::Message::Severity_Error); + if (mIcons.searchId(effect.mIcon) == -1) + { + std::string ddsIcon = effect.mIcon; + if (!(Misc::ResourceHelpers::changeExtensionToDds(ddsIcon) && mIcons.searchId(ddsIcon) != -1)) + messages.add(id, "Icon '" + effect.mIcon + "' does not exist", "", CSMDoc::Message::Severity_Error); + } } if (!effect.mParticle.empty()) { - const std::string error = checkTexture(effect.mParticle, false); - if (!error.empty()) - messages.add(id, error, "", CSMDoc::Message::Severity_Error); + if (mTextures.searchId(effect.mParticle) == -1) + { + std::string ddsParticle = effect.mParticle; + if (!(Misc::ResourceHelpers::changeExtensionToDds(ddsParticle) && mTextures.searchId(ddsParticle) != -1)) + messages.add(id, "Particle texture '" + effect.mParticle + "' does not exist", "", CSMDoc::Message::Severity_Error); + } } if (!effect.mCasting.empty()) @@ -119,31 +108,12 @@ void CSMTools::MagicEffectCheckStage::perform(int stage, CSMDoc::Messages &messa messages.add(id, error, "", CSMDoc::Message::Severity_Error); } - if (!effect.mCastSound.empty()) - { - const std::string error = checkSound(effect.mCastSound, "Casting sound"); - if (!error.empty()) - messages.add(id, error, "", CSMDoc::Message::Severity_Error); - } - - if (!effect.mHitSound.empty()) - { - const std::string error = checkSound(effect.mHitSound, "Hit sound"); - if (!error.empty()) - messages.add(id, error, "", CSMDoc::Message::Severity_Error); - } - - if (!effect.mAreaSound.empty()) - { - const std::string error = checkSound(effect.mAreaSound, "Area sound"); - if (!error.empty()) - messages.add(id, error, "", CSMDoc::Message::Severity_Error); - } - - if (!effect.mBoltSound.empty()) - { - const std::string error = checkSound(effect.mBoltSound, "Bolt sound"); - if (!error.empty()) - messages.add(id, error, "", CSMDoc::Message::Severity_Error); - } + if (!effect.mCastSound.empty() && mSounds.searchId(effect.mCastSound) == -1) + messages.add(id, "Casting sound '" + effect.mCastSound + "' does not exist", "", CSMDoc::Message::Severity_Error); + if (!effect.mHitSound.empty() && mSounds.searchId(effect.mHitSound) == -1) + messages.add(id, "Hit sound '" + effect.mHitSound + "' does not exist", "", CSMDoc::Message::Severity_Error); + if (!effect.mAreaSound.empty() && mSounds.searchId(effect.mAreaSound) == -1) + messages.add(id, "Area sound '" + effect.mAreaSound + "' does not exist", "", CSMDoc::Message::Severity_Error); + if (!effect.mBoltSound.empty() && mSounds.searchId(effect.mBoltSound) == -1) + messages.add(id, "Bolt sound '" + effect.mBoltSound + "' does not exist", "", CSMDoc::Message::Severity_Error); } diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index 60d4a6b58..4d68c93cd 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -723,7 +723,9 @@ void CSMTools::ReferenceableCheckStage::npcCheck ( messages.add(id, "Disposition is negative", "", CSMDoc::Message::Severity_Warning); if (reputation < 0) + { messages.add(id, "Reputation is negative", "", CSMDoc::Message::Severity_Warning); + } if (!npc.mFaction.empty()) { From 47b9008743b9a2a4d824f0768035f2932f672dd7 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 26 Aug 2018 22:16:50 +0300 Subject: [PATCH 034/163] Renovate reference record verifier --- apps/opencs/model/tools/referencecheck.cpp | 81 +++++++++------------- apps/opencs/model/tools/skillcheck.cpp | 2 - apps/opencs/model/tools/soundcheck.cpp | 1 - apps/opencs/model/tools/soundcheck.hpp | 6 +- 4 files changed, 33 insertions(+), 57 deletions(-) diff --git a/apps/opencs/model/tools/referencecheck.cpp b/apps/opencs/model/tools/referencecheck.cpp index dd93b4e9c..76bfeb3ba 100644 --- a/apps/opencs/model/tools/referencecheck.cpp +++ b/apps/opencs/model/tools/referencecheck.cpp @@ -28,76 +28,59 @@ void CSMTools::ReferenceCheckStage::perform(int stage, CSMDoc::Messages &message const CSMWorld::CellRef& cellRef = record.get(); const CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Reference, cellRef.mId); - // Check for empty reference id - if (cellRef.mRefID.empty()) { - messages.push_back(std::make_pair(id, "Instance is not based on an object")); - } else { + // Check reference id + if (cellRef.mRefID.empty()) + messages.add(id, "Instance is not based on an object", "", CSMDoc::Message::Severity_Error); + else + { // Check for non existing referenced object - if (mObjects.searchId(cellRef.mRefID) == -1) { - messages.push_back(std::make_pair(id, "Instance of a non-existent object '" + cellRef.mRefID + "'")); - } else { + if (mObjects.searchId(cellRef.mRefID) == -1) + messages.add(id, "Instance of a non-existent object '" + cellRef.mRefID + "'", "", CSMDoc::Message::Severity_Error); + else + { // Check if reference charge is valid for it's proper referenced type CSMWorld::RefIdData::LocalIndex localIndex = mDataSet.searchId(cellRef.mRefID); bool isLight = localIndex.second == CSMWorld::UniversalId::Type_Light; - if ((isLight && cellRef.mChargeFloat < -1) || (!isLight && cellRef.mChargeInt < -1)) { - std::string str = "Invalid charge: "; - if (localIndex.second == CSMWorld::UniversalId::Type_Light) - str += std::to_string(cellRef.mChargeFloat); - else - str += std::to_string(cellRef.mChargeInt); - messages.push_back(std::make_pair(id, str)); - } + if ((isLight && cellRef.mChargeFloat < -1) || (!isLight && cellRef.mChargeInt < -1)) + messages.add(id, "Invalid charge", "", CSMDoc::Message::Severity_Error); } } // If object have owner, check if that owner reference is valid if (!cellRef.mOwner.empty() && mObjects.searchId(cellRef.mOwner) == -1) - messages.push_back(std::make_pair(id, "Owner object '" + cellRef.mOwner + "' does not exist")); + messages.add(id, "Owner object '" + cellRef.mOwner + "' does not exist", "", CSMDoc::Message::Severity_Error); // If object have creature soul trapped, check if that creature reference is valid if (!cellRef.mSoul.empty()) if (mObjects.searchId(cellRef.mSoul) == -1) - messages.push_back(std::make_pair(id, "Trapped soul object '" + cellRef.mSoul + "' does not exist")); + messages.add(id, "Trapped soul object '" + cellRef.mOwner + "' does not exist", "", CSMDoc::Message::Severity_Error); - bool hasFaction = !cellRef.mFaction.empty(); - - // If object have faction, check if that faction reference is valid - if (hasFaction) - if (mFactions.searchId(cellRef.mFaction) == -1) - messages.push_back(std::make_pair(id, "Faction '" + cellRef.mFaction + "' does not exist")); - - // Check item's faction rank - if ((hasFaction && cellRef.mFactionRank < -1) || (!hasFaction && cellRef.mFactionRank != -2)) - messages.push_back(std::make_pair(id, "Invalid faction rank " + std::to_string(cellRef.mFactionRank))); - - // If door have destination cell, check if that reference is valid - if (!cellRef.mDestCell.empty()) - if (mCells.searchId(cellRef.mDestCell) == -1) - messages.push_back(std::make_pair(id, "Destination cell '" + cellRef.mDestCell + "' does not exist")); - - // Check if scale isn't negative - if (cellRef.mScale < 0) + if (cellRef.mFaction.empty()) { - std::string str = "Negative scale: "; - str += std::to_string(cellRef.mScale); - messages.push_back(std::make_pair(id, str)); + if (cellRef.mFactionRank != -2) + messages.add(id, "Reference without a faction has a faction rank", "", CSMDoc::Message::Severity_Error); } + else + { + if (mFactions.searchId(cellRef.mFaction) == -1) + messages.add(id, "Faction '" + cellRef.mFaction + "' does not exist", "", CSMDoc::Message::Severity_Error); + else if (cellRef.mFactionRank < -1) + messages.add(id, "Invalid faction rank", "", CSMDoc::Message::Severity_Error); + } + + if (!cellRef.mDestCell.empty() && mCells.searchId(cellRef.mDestCell) == -1) + messages.add(id, "Destination cell '" + cellRef.mDestCell + "' does not exist", "", CSMDoc::Message::Severity_Error); + + if (cellRef.mScale < 0) + messages.add(id, "Negative scale", "", CSMDoc::Message::Severity_Error); // Check if enchantement points aren't negative or are at full (-1) - if (cellRef.mEnchantmentCharge < 0 && cellRef.mEnchantmentCharge != -1) - { - std::string str = "Negative enchantment points: "; - str += std::to_string(cellRef.mEnchantmentCharge); - messages.push_back(std::make_pair(id, str)); - } + if (cellRef.mEnchantmentCharge < -1) + messages.add(id, "Negative number of enchantment points", "", CSMDoc::Message::Severity_Error); // Check if gold value isn't negative if (cellRef.mGoldValue < 0) - { - std::string str = "Negative gold value: "; - str += cellRef.mGoldValue; - messages.push_back(std::make_pair(id, str)); - } + messages.add(id, "Negative gold value", "", CSMDoc::Message::Severity_Error); } int CSMTools::ReferenceCheckStage::setup() diff --git a/apps/opencs/model/tools/skillcheck.cpp b/apps/opencs/model/tools/skillcheck.cpp index ab7df51cb..89eba011a 100644 --- a/apps/opencs/model/tools/skillcheck.cpp +++ b/apps/opencs/model/tools/skillcheck.cpp @@ -35,10 +35,8 @@ void CSMTools::SkillCheckStage::perform (int stage, CSMDoc::Messages& messages) messages.add(id, "Description is missing", "", CSMDoc::Message::Severity_Warning); for (int i=0; i<4; ++i) - { if (skill.mData.mUseValue[i]<0) { messages.add(id, "Usage experience value #" + std::to_string(i) + " is negative", "", CSMDoc::Message::Severity_Error); } - } } diff --git a/apps/opencs/model/tools/soundcheck.cpp b/apps/opencs/model/tools/soundcheck.cpp index 7bb27e984..09e5ecdbf 100644 --- a/apps/opencs/model/tools/soundcheck.cpp +++ b/apps/opencs/model/tools/soundcheck.cpp @@ -4,7 +4,6 @@ #include "../prefs/state.hpp" -#include "../world/resources.hpp" #include "../world/universalid.hpp" CSMTools::SoundCheckStage::SoundCheckStage (const CSMWorld::IdCollection &sounds, diff --git a/apps/opencs/model/tools/soundcheck.hpp b/apps/opencs/model/tools/soundcheck.hpp index 47c3249ce..fc5925717 100644 --- a/apps/opencs/model/tools/soundcheck.hpp +++ b/apps/opencs/model/tools/soundcheck.hpp @@ -3,15 +3,11 @@ #include +#include "../world/resources.hpp" #include "../world/idcollection.hpp" #include "../doc/stage.hpp" -namespace CSMWorld -{ - class Resources; -} - namespace CSMTools { /// \brief VerifyStage: make sure that sound records are internally consistent From dabdb0bfaf63d55ec72c678332e5153c19cdcc4b Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Wed, 29 Aug 2018 18:53:02 +0300 Subject: [PATCH 035/163] Get rid of deprecated Messages::push_back() --- apps/opencs/model/doc/messages.cpp | 5 ----- apps/opencs/model/doc/messages.hpp | 3 --- apps/opencs/model/tools/referenceablecheck.cpp | 4 ++-- 3 files changed, 2 insertions(+), 10 deletions(-) diff --git a/apps/opencs/model/doc/messages.cpp b/apps/opencs/model/doc/messages.cpp index 76bbb6f22..b70d44eda 100644 --- a/apps/opencs/model/doc/messages.cpp +++ b/apps/opencs/model/doc/messages.cpp @@ -35,11 +35,6 @@ void CSMDoc::Messages::add (const CSMWorld::UniversalId& id, const std::string& mMessages.push_back (Message (id, message, hint, severity)); } -void CSMDoc::Messages::push_back (const std::pair& data) -{ - add (data.first, data.second); -} - CSMDoc::Messages::Iterator CSMDoc::Messages::begin() const { return mMessages.begin(); diff --git a/apps/opencs/model/doc/messages.hpp b/apps/opencs/model/doc/messages.hpp index 4041e1a67..671ded82a 100644 --- a/apps/opencs/model/doc/messages.hpp +++ b/apps/opencs/model/doc/messages.hpp @@ -56,9 +56,6 @@ namespace CSMDoc const std::string& hint = "", Message::Severity severity = Message::Severity_Default); - /// \deprecated Use add instead. - void push_back (const std::pair& data); - Iterator begin() const; Iterator end() const; diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index 4d68c93cd..def6dece3 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -737,7 +737,7 @@ void CSMTools::ReferenceableCheckStage::npcCheck ( } if (npc.mHead.empty()) - messages.push_back (std::make_pair (id, "Head is missing")); + messages.add(id, "Head is missing", "", CSMDoc::Message::Severity_Error); else { if (mBodyParts.searchId(npc.mHead) == -1) @@ -746,7 +746,7 @@ void CSMTools::ReferenceableCheckStage::npcCheck ( } if (npc.mHair.empty()) - messages.push_back (std::make_pair (id, "Hair is missing")); + messages.add(id, "Hair is missing", "", CSMDoc::Message::Severity_Error); else { if (mBodyParts.searchId(npc.mHair) == -1) From 015cd6064faa44fd4969de80722b2f9ebaa79dbc Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Thu, 30 Aug 2018 01:21:27 +0300 Subject: [PATCH 036/163] Implement basic enchantment record verifier (feature #1617) --- apps/opencs/CMakeLists.txt | 2 +- apps/opencs/model/tools/enchantmentcheck.cpp | 42 ++++++++++++++++++++ apps/opencs/model/tools/enchantmentcheck.hpp | 30 ++++++++++++++ apps/opencs/model/tools/magiceffectcheck.cpp | 6 ++- apps/opencs/model/tools/tools.cpp | 3 ++ 5 files changed, 80 insertions(+), 3 deletions(-) create mode 100644 apps/opencs/model/tools/enchantmentcheck.cpp create mode 100644 apps/opencs/model/tools/enchantmentcheck.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index d1ebcde42..f2821f184 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -42,7 +42,7 @@ opencs_units_noqt (model/tools mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck birthsigncheck spellcheck referencecheck referenceablecheck scriptcheck bodypartcheck startscriptcheck search searchoperation searchstage pathgridcheck soundgencheck magiceffectcheck - mergestages gmstcheck topicinfocheck journalcheck + mergestages gmstcheck topicinfocheck journalcheck enchantmentcheck ) opencs_hdrs_noqt (model/tools diff --git a/apps/opencs/model/tools/enchantmentcheck.cpp b/apps/opencs/model/tools/enchantmentcheck.cpp new file mode 100644 index 000000000..e1dca9331 --- /dev/null +++ b/apps/opencs/model/tools/enchantmentcheck.cpp @@ -0,0 +1,42 @@ +#include "enchantmentcheck.hpp" + +#include "../prefs/state.hpp" + +#include "../world/universalid.hpp" + +CSMTools::EnchantmentCheckStage::EnchantmentCheckStage (const CSMWorld::IdCollection& enchantments) + : mEnchantments (enchantments) +{ + mIgnoreBaseRecords = false; +} + +int CSMTools::EnchantmentCheckStage::setup() +{ + mIgnoreBaseRecords = CSMPrefs::get()["Reports"]["ignore-base-records"].isTrue(); + + return mEnchantments.getSize(); +} + +void CSMTools::EnchantmentCheckStage::perform (int stage, CSMDoc::Messages& messages) +{ + const CSMWorld::Record& record = mEnchantments.getRecord (stage); + + // Skip "Base" records (setting!) and "Deleted" records + if ((mIgnoreBaseRecords && record.mState == CSMWorld::RecordBase::State_BaseOnly) || record.isDeleted()) + return; + + const ESM::Enchantment& enchantment = record.get(); + + CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Enchantment, enchantment.mId); + + if (enchantment.mData.mType < 0 || enchantment.mData.mType > 3) + messages.add(id, "Invalid type", "", CSMDoc::Message::Severity_Error); + + if (enchantment.mData.mCost < 0) + messages.add(id, "Cost is negative", "", CSMDoc::Message::Severity_Error); + + if (enchantment.mData.mCharge < 0) + messages.add(id, "Charge is negative", "", CSMDoc::Message::Severity_Error); + + /// \todo Check effects list for validity +} diff --git a/apps/opencs/model/tools/enchantmentcheck.hpp b/apps/opencs/model/tools/enchantmentcheck.hpp new file mode 100644 index 000000000..ba8ae64aa --- /dev/null +++ b/apps/opencs/model/tools/enchantmentcheck.hpp @@ -0,0 +1,30 @@ +#ifndef CSM_TOOLS_ENCHANTMENTCHECK_H +#define CSM_TOOLS_ENCHANTMENTCHECK_H + +#include + +#include "../world/idcollection.hpp" + +#include "../doc/stage.hpp" + +namespace CSMTools +{ + /// \brief Make sure that enchantment records are correct + class EnchantmentCheckStage : public CSMDoc::Stage + { + const CSMWorld::IdCollection& mEnchantments; + bool mIgnoreBaseRecords; + + public: + + EnchantmentCheckStage (const CSMWorld::IdCollection& enchantments); + + virtual int setup(); + ///< \return number of steps + + virtual void perform (int stage, CSMDoc::Messages& messages); + ///< Messages resulting from this tage will be appended to \a messages. + }; +} + +#endif diff --git a/apps/opencs/model/tools/magiceffectcheck.cpp b/apps/opencs/model/tools/magiceffectcheck.cpp index b62e791a4..f55fb14ee 100644 --- a/apps/opencs/model/tools/magiceffectcheck.cpp +++ b/apps/opencs/model/tools/magiceffectcheck.cpp @@ -9,8 +9,10 @@ std::string CSMTools::MagicEffectCheckStage::checkObject(const std::string &id, const std::string &column) const { CSMWorld::RefIdData::LocalIndex index = mObjects.getDataSet().searchId(id); - if (index.first == -1) return (column + " '" + id + "' " + "does not exist"); - else if (index.second != type.getType()) return (column + " '" + id + "' " + "does not have " + type.getTypeName() + " type"); + if (index.first == -1) + return (column + " '" + id + "' does not exist"); + else if (index.second != type.getType()) + return (column + " '" + id + "' does not have " + type.getTypeName() + " type"); return std::string(); } diff --git a/apps/opencs/model/tools/tools.cpp b/apps/opencs/model/tools/tools.cpp index b9a0abb75..07a721e8e 100644 --- a/apps/opencs/model/tools/tools.cpp +++ b/apps/opencs/model/tools/tools.cpp @@ -32,6 +32,7 @@ #include "gmstcheck.hpp" #include "topicinfocheck.hpp" #include "journalcheck.hpp" +#include "enchantmentcheck.hpp" CSMDoc::OperationHolder *CSMTools::Tools::get (int type) { @@ -128,6 +129,8 @@ CSMDoc::OperationHolder *CSMTools::Tools::getVerifier() mVerifierOperation->appendStage (new JournalCheckStage(mData.getJournals(), mData.getJournalInfos())); + mVerifierOperation->appendStage (new EnchantmentCheckStage(mData.getEnchantments())); + mVerifier.setOperation (mVerifierOperation); } From 5d1c1f25f79ed21432503ebb3fdc6b4a935e3352 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Mon, 3 Sep 2018 16:57:01 +0300 Subject: [PATCH 037/163] Remove now redundant NPC fields checks --- .../opencs/model/tools/referenceablecheck.cpp | 21 ++----------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index def6dece3..d31b8b7d0 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -667,9 +667,6 @@ void CSMTools::ReferenceableCheckStage::npcCheck ( return; short level(npc.mNpdt.mLevel); - char disposition(npc.mNpdt.mDisposition); - char reputation(npc.mNpdt.mReputation); - char rank(npc.mNpdt.mRank); int gold(npc.mNpdt.mGold); if (npc.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS) //12 = autocalculated @@ -719,22 +716,8 @@ void CSMTools::ReferenceableCheckStage::npcCheck ( else if (mRaces.searchId (npc.mRace) == -1) messages.add(id, "Race '" + npc.mRace + "' does not exist", "", CSMDoc::Message::Severity_Error); - if (disposition < 0) - messages.add(id, "Disposition is negative", "", CSMDoc::Message::Severity_Warning); - - if (reputation < 0) - { - messages.add(id, "Reputation is negative", "", CSMDoc::Message::Severity_Warning); - } - - if (!npc.mFaction.empty()) - { - if (rank < 0) - messages.add(id, "Faction rank is negative", "", CSMDoc::Message::Severity_Warning); - - if (mFactions.searchId(npc.mFaction) == -1) - messages.add(id, "Faction '" + npc.mFaction + "' does not exist", "", CSMDoc::Message::Severity_Error); - } + if (!npc.mFaction.empty() && mFactions.searchId(npc.mFaction) == -1) + messages.add(id, "Faction '" + npc.mFaction + "' does not exist", "", CSMDoc::Message::Severity_Error); if (npc.mHead.empty()) messages.add(id, "Head is missing", "", CSMDoc::Message::Severity_Error); From d6560d3f20dc5dd889088bc796e5bf0032610508 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Tue, 4 Sep 2018 16:08:09 +0300 Subject: [PATCH 038/163] Make several messages more strict and clean up topic info verifier --- apps/opencs/model/tools/classcheck.cpp | 11 ++--- apps/opencs/model/tools/factioncheck.cpp | 6 +-- .../opencs/model/tools/referenceablecheck.cpp | 2 +- apps/opencs/model/tools/topicinfocheck.cpp | 45 +++++-------------- 4 files changed, 18 insertions(+), 46 deletions(-) diff --git a/apps/opencs/model/tools/classcheck.cpp b/apps/opencs/model/tools/classcheck.cpp index 25cca8fcd..a82121597 100644 --- a/apps/opencs/model/tools/classcheck.cpp +++ b/apps/opencs/model/tools/classcheck.cpp @@ -36,12 +36,7 @@ void CSMTools::ClassCheckStage::perform (int stage, CSMDoc::Messages& messages) // A class should have a name if (class_.mName.empty()) - { - if (class_.mData.mIsPlayable != 0) - messages.add(id, "Name of a playable class is missing", "", CSMDoc::Message::Severity_Error); - else - messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Warning); - } + messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Error); // A playable class should have a description if (class_.mData.mIsPlayable != 0 && class_.mDescription.empty()) @@ -56,7 +51,7 @@ void CSMTools::ClassCheckStage::perform (int stage, CSMDoc::Messages& messages) if (class_.mData.mAttribute[0]==class_.mData.mAttribute[1] && class_.mData.mAttribute[0]!=-1) { - messages.add(id, "Same attribute is listed twice", "", CSMDoc::Message::Severity_Warning); + messages.add(id, "Same attribute is listed twice", "", CSMDoc::Message::Severity_Error); } // test for non-unique skill @@ -69,6 +64,6 @@ void CSMTools::ClassCheckStage::perform (int stage, CSMDoc::Messages& messages) for (auto &skill : skills) if (skill.second>1) { - messages.add(id, "Skill " + ESM::Skill::indexToId (skill.first) + " is listed more than once", "", CSMDoc::Message::Severity_Warning); + messages.add(id, "Skill " + ESM::Skill::indexToId (skill.first) + " is listed more than once", "", CSMDoc::Message::Severity_Error); } } diff --git a/apps/opencs/model/tools/factioncheck.cpp b/apps/opencs/model/tools/factioncheck.cpp index 5bd613851..0ee245ad4 100644 --- a/apps/opencs/model/tools/factioncheck.cpp +++ b/apps/opencs/model/tools/factioncheck.cpp @@ -37,12 +37,12 @@ void CSMTools::FactionCheckStage::perform (int stage, CSMDoc::Messages& messages // test for empty name if (faction.mName.empty()) - messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Warning); + messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Error); // test for invalid attributes if (faction.mData.mAttribute[0]==faction.mData.mAttribute[1] && faction.mData.mAttribute[0]!=-1) { - messages.add(id, "Same attribute is listed twice", "", CSMDoc::Message::Severity_Warning); + messages.add(id, "Same attribute is listed twice", "", CSMDoc::Message::Severity_Error); } // test for non-unique skill @@ -55,7 +55,7 @@ void CSMTools::FactionCheckStage::perform (int stage, CSMDoc::Messages& messages for (auto &skill : skills) if (skill.second>1) { - messages.add(id, "Skill " + ESM::Skill::indexToId (skill.first) + " is listed more than once", "", CSMDoc::Message::Severity_Warning); + messages.add(id, "Skill " + ESM::Skill::indexToId (skill.first) + " is listed more than once", "", CSMDoc::Message::Severity_Error); } /// \todo check data members that can't be edited in the table view diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index d31b8b7d0..fdbab7fd0 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -475,7 +475,7 @@ void CSMTools::ReferenceableCheckStage::creatureCheck ( if (creature.mData.mAttack[i] < 0) messages.add(id, "Attack " + std::to_string(i/2 + 1) + " has negative" + (i % 2 == 0 ? " minimum " : " maximum ") + "damage", "", CSMDoc::Message::Severity_Error); if (i % 2 == 0 && creature.mData.mAttack[i] > creature.mData.mAttack[i+1]) - messages.add(id, "Attack " + std::to_string(i/2 + 1) + " has minimum damage higher than maximum damage", "", CSMDoc::Message::Severity_Warning); + messages.add(id, "Attack " + std::to_string(i/2 + 1) + " has minimum damage higher than maximum damage", "", CSMDoc::Message::Severity_Error); } if (creature.mData.mGold < 0) diff --git a/apps/opencs/model/tools/topicinfocheck.cpp b/apps/opencs/model/tools/topicinfocheck.cpp index 9a31db72b..fe9bc991d 100644 --- a/apps/opencs/model/tools/topicinfocheck.cpp +++ b/apps/opencs/model/tools/topicinfocheck.cpp @@ -133,7 +133,6 @@ void CSMTools::TopicInfoCheckStage::perform(int stage, CSMDoc::Messages& message if (topicInfo.mData.mGender < -1 || topicInfo.mData.mGender > 1) { - std::ostringstream stream; messages.add(id, "Gender is invalid", "", CSMDoc::Message::Severity_Error); } @@ -170,16 +169,12 @@ bool CSMTools::TopicInfoCheckStage::verifyActor(const std::string& actor, const if (index.first == -1) { - std::ostringstream stream; - stream << "Actor '" << actor << "' does not exist"; - messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); + messages.add(id, "Actor '" + actor + "' does not exist", "", CSMDoc::Message::Severity_Error); return false; } else if (mReferencables.getRecord(index).isDeleted()) { - std::ostringstream stream; - stream << "Deleted actor '" << actor << "' is being referenced"; - messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); + messages.add(id, "Deleted actor '" + actor + "' is being referenced", "", CSMDoc::Message::Severity_Error); return false; } else if (index.second != CSMWorld::UniversalId::Type_Npc && index.second != CSMWorld::UniversalId::Type_Creature) @@ -199,9 +194,7 @@ bool CSMTools::TopicInfoCheckStage::verifyCell(const std::string& cell, const CS { if (mCellNames.find(cell) == mCellNames.end()) { - std::ostringstream stream; - stream << "Cell '" << cell << "' does not exist"; - messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); + messages.add(id, "Cell '" + cell + "' does not exist", "", CSMDoc::Message::Severity_Error); return false; } @@ -236,7 +229,7 @@ bool CSMTools::TopicInfoCheckStage::verifyFactionRank(const std::string& faction stream << "Faction rank is set to " << rank << " which is more than the maximum of " << limit - 1 << " for the '" << factionName << "' faction"; - messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Warning); + messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); return false; } @@ -250,16 +243,12 @@ bool CSMTools::TopicInfoCheckStage::verifyItem(const std::string& item, const CS if (index.first == -1) { - std::ostringstream stream; - stream << "Item '" << item << "' does not exist"; - messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); + messages.add(id, ("Item '" + item + "' does not exist"), "", CSMDoc::Message::Severity_Error); return false; } else if (mReferencables.getRecord(index).isDeleted()) { - std::ostringstream stream; - stream << "Deleted item '" << item << "' is being referenced"; - messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); + messages.add(id, ("Deleted item '" + item + "' is being referenced"), "", CSMDoc::Message::Severity_Error); return false; } else @@ -327,18 +316,12 @@ bool CSMTools::TopicInfoCheckStage::verifySelectStruct(const ESM::DialInfo::Sele } else if (infoCondition.conditionIsAlwaysTrue()) { - std::ostringstream stream; - stream << "Condition '" << infoCondition.toString() << "' is always true"; - - messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Warning); + messages.add(id, "Condition '" + infoCondition.toString() + "' is always true", "", CSMDoc::Message::Severity_Warning); return false; } else if (infoCondition.conditionIsNeverTrue()) { - std::ostringstream stream; - stream << "Condition '" << infoCondition.toString() << "' is never true"; - - messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Warning); + messages.add(id, "Condition '" + infoCondition.toString() + "' is never true", "", CSMDoc::Message::Severity_Warning); return false; } @@ -397,9 +380,7 @@ bool CSMTools::TopicInfoCheckStage::verifySound(const std::string& sound, const { if (mSoundFiles.searchId(sound) == -1) { - std::ostringstream stream; - stream << "Sound file '" << sound << "' does not exist"; - messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); + messages.add(id, "Sound file '" + sound + "' does not exist", "", CSMDoc::Message::Severity_Error); return false; } @@ -414,16 +395,12 @@ bool CSMTools::TopicInfoCheckStage::verifyId(const std::string& name, const CSMW if (index == -1) { - std::ostringstream stream; - stream << T::getRecordType() + " '" << name << "' does not exist"; - messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); + messages.add(id, T::getRecordType() + " '" + name + "' does not exist", "", CSMDoc::Message::Severity_Error); return false; } else if (collection.getRecord(index).isDeleted()) { - std::ostringstream stream; - stream << "Deleted " << T::getRecordType() << " record '" << name << "' is being referenced"; - messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); + messages.add(id, "Deleted " + T::getRecordType() + " record '" + name + "' is being referenced", "", CSMDoc::Message::Severity_Error); return false; } From c025427575e7279f2871a982f2a6213f3a4b9642 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Tue, 11 Sep 2018 21:40:41 +0300 Subject: [PATCH 039/163] Implement enchantment record effect list verification (feature #1617) --- apps/opencs/model/tools/enchantmentcheck.cpp | 42 +++++++++++++++++++- apps/opencs/model/tools/enchantmentcheck.hpp | 1 + 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/apps/opencs/model/tools/enchantmentcheck.cpp b/apps/opencs/model/tools/enchantmentcheck.cpp index e1dca9331..28f2b32cb 100644 --- a/apps/opencs/model/tools/enchantmentcheck.cpp +++ b/apps/opencs/model/tools/enchantmentcheck.cpp @@ -38,5 +38,45 @@ void CSMTools::EnchantmentCheckStage::perform (int stage, CSMDoc::Messages& mess if (enchantment.mData.mCharge < 0) messages.add(id, "Charge is negative", "", CSMDoc::Message::Severity_Error); - /// \todo Check effects list for validity + if (enchantment.mData.mCost > enchantment.mData.mCharge) + messages.add(id, "Cost is higher than charge", "", CSMDoc::Message::Severity_Error); + + if (enchantment.mEffects.mList.empty()) + { + messages.add(id, "Enchantment doesn't have any magic effects", "", CSMDoc::Message::Severity_Warning); + } + else + { + std::vector::const_iterator effect = enchantment.mEffects.mList.begin(); + + for (size_t i = 1; i <= enchantment.mEffects.mList.size(); i++) + { + const std::string number = std::to_string(i); + // At the time of writing this effects, attributes and skills are hardcoded + if (effect->mEffectID < 0 || effect->mEffectID > 142) + { + messages.add(id, "Effect #" + number + " is invalid", "", CSMDoc::Message::Severity_Error); + ++effect; + continue; + } + + if (effect->mSkill < -1 || effect->mSkill > 26) + messages.add(id, "Effect #" + number + " affected skill is invalid", "", CSMDoc::Message::Severity_Error); + if (effect->mAttribute < -1 || effect->mAttribute > 7) + messages.add(id, "Effect #" + number + " affected attribute is invalid", "", CSMDoc::Message::Severity_Error); + if (effect->mRange < 0 || effect->mRange > 2) + messages.add(id, "Effect #" + number + " range is invalid", "", CSMDoc::Message::Severity_Error); + if (effect->mArea < 0) + messages.add(id, "Effect #" + number + " area is negative", "", CSMDoc::Message::Severity_Error); + if (effect->mDuration < 0) + messages.add(id, "Effect #" + number + " duration is negative", "", CSMDoc::Message::Severity_Error); + if (effect->mMagnMin < 0) + messages.add(id, "Effect #" + number + " minimum magnitude is negative", "", CSMDoc::Message::Severity_Error); + if (effect->mMagnMax < 0) + messages.add(id, "Effect #" + number + " maximum magnitude is negative", "", CSMDoc::Message::Severity_Error); + if (effect->mMagnMin > effect->mMagnMax) + messages.add(id, "Effect #" + number + " minimum magnitude is higher than maximum magnitude", "", CSMDoc::Message::Severity_Error); + ++effect; + } + } } diff --git a/apps/opencs/model/tools/enchantmentcheck.hpp b/apps/opencs/model/tools/enchantmentcheck.hpp index ba8ae64aa..3bd85326f 100644 --- a/apps/opencs/model/tools/enchantmentcheck.hpp +++ b/apps/opencs/model/tools/enchantmentcheck.hpp @@ -24,6 +24,7 @@ namespace CSMTools virtual void perform (int stage, CSMDoc::Messages& messages); ///< Messages resulting from this tage will be appended to \a messages. + }; } From 5a86554f97f87c52add041865a4fdcd2a5dee6bd Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Fri, 14 Sep 2018 21:42:11 +0300 Subject: [PATCH 040/163] Cleanup --- apps/opencs/model/tools/birthsigncheck.cpp | 6 +----- apps/opencs/model/tools/factioncheck.cpp | 2 -- apps/opencs/model/tools/journalcheck.cpp | 7 ++----- apps/opencs/model/tools/magiceffectcheck.hpp | 2 -- apps/opencs/model/tools/racecheck.cpp | 10 ++-------- apps/opencs/model/tools/regioncheck.cpp | 5 ----- apps/opencs/model/tools/skillcheck.cpp | 4 +--- apps/opencs/model/tools/soundcheck.cpp | 4 +--- apps/opencs/model/tools/soundgencheck.cpp | 2 -- 9 files changed, 7 insertions(+), 35 deletions(-) diff --git a/apps/opencs/model/tools/birthsigncheck.cpp b/apps/opencs/model/tools/birthsigncheck.cpp index a0ae4b11f..f91fc22f6 100644 --- a/apps/opencs/model/tools/birthsigncheck.cpp +++ b/apps/opencs/model/tools/birthsigncheck.cpp @@ -1,6 +1,5 @@ #include "birthsigncheck.hpp" -#include #include #include "../prefs/state.hpp" @@ -42,11 +41,8 @@ void CSMTools::BirthsignCheckStage::perform (int stage, CSMDoc::Messages& messag if (birthsign.mTexture.empty()) messages.add(id, "Image is missing", "", CSMDoc::Message::Severity_Error); - else + else if (mTextures.searchId(birthsign.mTexture) == -1) { - if (mTextures.searchId(birthsign.mTexture) != -1) - return; - std::string ddsTexture = birthsign.mTexture; if (!(Misc::ResourceHelpers::changeExtensionToDds(ddsTexture) && mTextures.searchId(ddsTexture) != -1)) messages.add(id, "Image '" + birthsign.mTexture + "' does not exist", "", CSMDoc::Message::Severity_Error); diff --git a/apps/opencs/model/tools/factioncheck.cpp b/apps/opencs/model/tools/factioncheck.cpp index 0ee245ad4..8a198e953 100644 --- a/apps/opencs/model/tools/factioncheck.cpp +++ b/apps/opencs/model/tools/factioncheck.cpp @@ -1,9 +1,7 @@ #include "factioncheck.hpp" -#include #include -#include #include #include "../prefs/state.hpp" diff --git a/apps/opencs/model/tools/journalcheck.cpp b/apps/opencs/model/tools/journalcheck.cpp index dc15b26ee..ae83abfa0 100644 --- a/apps/opencs/model/tools/journalcheck.cpp +++ b/apps/opencs/model/tools/journalcheck.cpp @@ -1,7 +1,6 @@ #include "journalcheck.hpp" #include -#include #include "../prefs/state.hpp" @@ -63,12 +62,10 @@ void CSMTools::JournalCheckStage::perform(int stage, CSMDoc::Messages& messages) std::pair::iterator, bool> result = questIndices.insert(journalInfo.mData.mJournalIndex); // Duplicate index - if (result.second == false) + if (!result.second) { CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_JournalInfo, journalInfo.mId); - std::ostringstream stream; - stream << "Duplicated quest index " << journalInfo.mData.mJournalIndex; - messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); + messages.add(id, "Duplicated quest index " + std::to_string(journalInfo.mData.mJournalIndex), "", CSMDoc::Message::Severity_Error); } } diff --git a/apps/opencs/model/tools/magiceffectcheck.hpp b/apps/opencs/model/tools/magiceffectcheck.hpp index 22dad3db9..a52723b0f 100644 --- a/apps/opencs/model/tools/magiceffectcheck.hpp +++ b/apps/opencs/model/tools/magiceffectcheck.hpp @@ -23,9 +23,7 @@ namespace CSMTools bool mIgnoreBaseRecords; private: - std::string checkTexture(const std::string &texture, bool isIcon) const; std::string checkObject(const std::string &id, const CSMWorld::UniversalId &type, const std::string &column) const; - std::string checkSound(const std::string &id, const std::string &column) const; public: MagicEffectCheckStage(const CSMWorld::IdCollection &effects, diff --git a/apps/opencs/model/tools/racecheck.cpp b/apps/opencs/model/tools/racecheck.cpp index 3b487b925..6585a31cc 100644 --- a/apps/opencs/model/tools/racecheck.cpp +++ b/apps/opencs/model/tools/racecheck.cpp @@ -1,9 +1,5 @@ #include "racecheck.hpp" -#include - -#include - #include "../prefs/state.hpp" #include "../world/universalid.hpp" @@ -28,10 +24,8 @@ void CSMTools::RaceCheckStage::performPerRecord (int stage, CSMDoc::Messages& me CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Race, race.mId); // test for empty name and description - if (race.mName.empty() && race.mData.mFlags & 0x1) - messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Error); - else if (race.mName.empty()) - messages.add(id, "Name is missing", "", CSMDoc::Message::Severity_Warning); + if (race.mName.empty()) + messages.add(id, "Name is missing", "", (race.mData.mFlags & 0x1) ? CSMDoc::Message::Severity_Error : CSMDoc::Message::Severity_Warning); if (race.mDescription.empty()) messages.add(id, "Description is missing", "", CSMDoc::Message::Severity_Warning); diff --git a/apps/opencs/model/tools/regioncheck.cpp b/apps/opencs/model/tools/regioncheck.cpp index 8375e3f0a..0b6537d7f 100644 --- a/apps/opencs/model/tools/regioncheck.cpp +++ b/apps/opencs/model/tools/regioncheck.cpp @@ -1,10 +1,5 @@ #include "regioncheck.hpp" -#include -#include - -#include - #include "../prefs/state.hpp" #include "../world/universalid.hpp" diff --git a/apps/opencs/model/tools/skillcheck.cpp b/apps/opencs/model/tools/skillcheck.cpp index 89eba011a..c5b38dc1e 100644 --- a/apps/opencs/model/tools/skillcheck.cpp +++ b/apps/opencs/model/tools/skillcheck.cpp @@ -1,7 +1,5 @@ #include "skillcheck.hpp" -#include - #include "../prefs/state.hpp" #include "../world/universalid.hpp" @@ -37,6 +35,6 @@ void CSMTools::SkillCheckStage::perform (int stage, CSMDoc::Messages& messages) for (int i=0; i<4; ++i) if (skill.mData.mUseValue[i]<0) { - messages.add(id, "Usage experience value #" + std::to_string(i) + " is negative", "", CSMDoc::Message::Severity_Error); + messages.add(id, "Use value #" + std::to_string(i) + " is negative", "", CSMDoc::Message::Severity_Error); } } diff --git a/apps/opencs/model/tools/soundcheck.cpp b/apps/opencs/model/tools/soundcheck.cpp index 09e5ecdbf..c0d893f1a 100644 --- a/apps/opencs/model/tools/soundcheck.cpp +++ b/apps/opencs/model/tools/soundcheck.cpp @@ -1,7 +1,5 @@ #include "soundcheck.hpp" -#include - #include "../prefs/state.hpp" #include "../world/universalid.hpp" @@ -35,7 +33,7 @@ void CSMTools::SoundCheckStage::perform (int stage, CSMDoc::Messages& messages) if (sound.mData.mMinRange>sound.mData.mMaxRange) { - messages.add(id, "Minimum range larger than maximum range", "", CSMDoc::Message::Severity_Warning); + messages.add(id, "Minimum range is larger than maximum range", "", CSMDoc::Message::Severity_Warning); } if (sound.mSound.empty()) diff --git a/apps/opencs/model/tools/soundgencheck.cpp b/apps/opencs/model/tools/soundgencheck.cpp index 7b6cf7a4b..ec29e23fe 100644 --- a/apps/opencs/model/tools/soundgencheck.cpp +++ b/apps/opencs/model/tools/soundgencheck.cpp @@ -1,7 +1,5 @@ #include "soundgencheck.hpp" -#include - #include "../prefs/state.hpp" #include "../world/refiddata.hpp" From 59092978091fba6e756408af80f9ad58de2a03b8 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 15 Sep 2018 23:20:28 +0300 Subject: [PATCH 041/163] Make underwater SFX always apply based on camera position (bug #4532) --- CHANGELOG.md | 1 + apps/openmw/mwworld/worldimp.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6bea04fcb..4c304ed9b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -93,6 +93,7 @@ Bug #4510: Division by zero in MWMechanics::CreatureStats::setAttribute Bug #4519: Knockdown does not discard movement in the 1st-person mode Bug #4531: Movement does not reset idle animations + Bug #4532: Underwater sfx isn't tied to 3rd person camera Bug #4539: Paper Doll is affected by GUI scaling Bug #4543: Picking cursed items through inventory (menumode) makes it disappear Bug #4545: Creatures flee from werewolves diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 9e08a2563..d1f274787 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1789,7 +1789,7 @@ namespace MWWorld osg::Vec3f forward = listenerOrient * osg::Vec3f(0,1,0); osg::Vec3f up = listenerOrient * osg::Vec3f(0,0,1); - bool underwater = isUnderwater(getPlayerPtr().getCell(), listenerPos); + bool underwater = isUnderwater(getPlayerPtr().getCell(), mRendering->getCameraPosition()); MWBase::Environment::get().getSoundManager()->setListenerPosDir(listenerPos, forward, up, underwater); } From ae1c054635b6268dc19aab85397ad884aa81a903 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 15 Sep 2018 19:38:21 +0400 Subject: [PATCH 042/163] Make GetPCJumping return true only when jumping (bug #4641) --- CHANGELOG.md | 1 + apps/openmw/mwmechanics/character.cpp | 21 +++++++++++++-------- apps/openmw/mwmechanics/creaturestats.cpp | 6 +++++- apps/openmw/mwmechanics/creaturestats.hpp | 2 +- apps/openmw/mwphysics/physicssystem.cpp | 3 ++- apps/openmw/mwscript/miscextensions.cpp | 3 +-- apps/openmw/mwworld/actionteleport.cpp | 2 +- apps/openmw/mwworld/player.cpp | 14 +++++++++++++- apps/openmw/mwworld/player.hpp | 4 ++++ 9 files changed, 41 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a01e2c746..36c7e19c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -119,6 +119,7 @@ Bug #4622: Recharging enchanted items with Soul Gems does not award experience if it fails Bug #4628: NPC record reputation, disposition and faction rank should have unsigned char type Bug #4633: Sneaking stance affects speed even if the actor is not able to crouch + Bug #4641: GetPCJumping is handled incorrectly Feature #912: Editor: Add missing icons to UniversalId tables Feature #1645: Casting effects from objects Feature #2606: Editor: Implemented (optional) case sensitive global search diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 123f7bf59..af8ed706d 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1850,7 +1850,8 @@ void CharacterController::update(float duration) if (isKnockedOut()) mTimeUntilWake -= duration; - bool godmode = mPtr == MWMechanics::getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState(); + bool isPlayer = mPtr == MWMechanics::getPlayer(); + bool godmode = isPlayer && MWBase::Environment::get().getWorld()->getGodModeState(); if(!cls.isActor()) updateAnimQueue(); @@ -1915,7 +1916,7 @@ void CharacterController::update(float duration) isrunning = isrunning && mHasMovedInXY; // advance athletics - if(mHasMovedInXY && mPtr == getPlayer()) + if(mHasMovedInXY && isPlayer) { if(inwater) { @@ -2011,8 +2012,12 @@ void CharacterController::update(float duration) } // advance acrobatics - if (mPtr == getPlayer()) + // also set jumping flag to allow GetPCJumping works + if (isPlayer) + { cls.skillUsageSucceeded(mPtr, ESM::Skill::Acrobatics, 0); + MWBase::Environment::get().getWorld()->getPlayer().setJumping(true); + } // decrease fatigue const float fatigueJumpBase = gmst.find("fFatigueJumpBase")->mValue.getFloat(); @@ -2035,7 +2040,7 @@ void CharacterController::update(float duration) jumpstate = JumpState_Landing; vec.z() = 0.0f; - float height = cls.getCreatureStats(mPtr).land(); + float height = cls.getCreatureStats(mPtr).land(isPlayer); float healthLost = getFallDamage(mPtr, height); if (healthLost > 0.0f) @@ -2058,7 +2063,7 @@ void CharacterController::update(float duration) else { // report acrobatics progression - if (mPtr == getPlayer()) + if (isPlayer) cls.skillUsageSucceeded(mPtr, ESM::Skill::Acrobatics, 1); } } @@ -2080,7 +2085,7 @@ void CharacterController::update(float duration) // Do not play turning animation for player if rotation speed is very slow. // Actual threshold should take framerate in account. float rotationThreshold = 0; - if (mPtr == getPlayer()) + if (isPlayer) rotationThreshold = 0.015 * 60 * duration; if(std::abs(vec.x()/2.0f) > std::abs(vec.y())) @@ -2109,7 +2114,7 @@ void CharacterController::update(float duration) { // It seems only bipedal actors use turning animations. // Also do not use turning animations in the first-person view and when sneaking. - bool isFirstPlayer = mPtr == getPlayer() && MWBase::Environment::get().getWorld()->isFirstPerson(); + bool isFirstPlayer = isPlayer && MWBase::Environment::get().getWorld()->isFirstPerson(); if (!sneak && !isFirstPlayer && mPtr.getClass().isBipedal(mPtr)) { if(rot.z() > rotationThreshold) @@ -2121,7 +2126,7 @@ void CharacterController::update(float duration) } // Player can not use smooth turning as NPCs, so we play turning animation a bit to avoid jittering - if (mPtr == getPlayer()) + if (isPlayer) { float threshold = mCurrentMovement.find("swim") == std::string::npos ? 0.4f : 0.8f; float complete; diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 2994eac28..7cc6ea784 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -7,6 +7,7 @@ #include #include "../mwworld/esmstore.hpp" +#include "../mwworld/player.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -387,8 +388,11 @@ namespace MWMechanics mFallHeight += height; } - float CreatureStats::land() + float CreatureStats::land(bool isPlayer) { + if (isPlayer) + MWBase::Environment::get().getWorld()->getPlayer().setJumping(false); + float height = mFallHeight; mFallHeight = 0; return height; diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 057a6f602..503ac7d68 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -95,7 +95,7 @@ namespace MWMechanics /// Reset the fall height /// @return total fall height - float land(); + float land(bool isPlayer=false); const AttributeValue & getAttribute(int index) const; diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 90668914f..90d25ee3e 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -1394,6 +1394,7 @@ namespace MWPhysics mStandingCollisions.clear(); } + const MWWorld::Ptr player = MWMechanics::getPlayer(); const MWBase::World *world = MWBase::Environment::get().getWorld(); PtrVelocityList::iterator iter = mMovementQueue.begin(); for(;iter != mMovementQueue.end();++iter) @@ -1451,7 +1452,7 @@ namespace MWPhysics MWMechanics::CreatureStats& stats = iter->first.getClass().getCreatureStats(iter->first); if ((wasOnGround && physicActor->getOnGround()) || flying || world->isSwimming(iter->first) || slowFall < 1) - stats.land(); + stats.land(iter->first == player); else if (heightDiff < 0) stats.addToFallHeight(-heightDiff); diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 8d592f29c..7250e9fcf 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -107,8 +107,7 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { MWBase::World* world = MWBase::Environment::get().getWorld(); - MWWorld::Ptr player = world->getPlayerPtr(); - runtime.push (!world->isOnGround(player) && !world->isFlying(player)); + runtime.push (world->getPlayer().getJumping()); } }; diff --git a/apps/openmw/mwworld/actionteleport.cpp b/apps/openmw/mwworld/actionteleport.cpp index 93705f005..f54edc8cb 100644 --- a/apps/openmw/mwworld/actionteleport.cpp +++ b/apps/openmw/mwworld/actionteleport.cpp @@ -36,7 +36,7 @@ namespace MWWorld void ActionTeleport::teleport(const Ptr &actor) { MWBase::World* world = MWBase::Environment::get().getWorld(); - actor.getClass().getCreatureStats(actor).land(); + actor.getClass().getCreatureStats(actor).land(actor == world->getPlayerPtr()); if(actor == world->getPlayerPtr()) { world->getPlayer().setTeleported(true); diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index 389f59983..49b7dd8bb 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -36,7 +36,8 @@ namespace MWWorld mTeleported(false), mCurrentCrimeId(-1), mPaidCrimeId(-1), - mAttackingOrSpell(false) + mAttackingOrSpell(false), + mJumping(false) { ESM::CellRef cellRef; cellRef.blank(); @@ -255,6 +256,16 @@ namespace MWWorld return mAttackingOrSpell; } + void Player::setJumping(bool jumping) + { + mJumping = jumping; + } + + bool Player::getJumping() const + { + return mJumping; + } + bool Player::isInCombat() { return MWBase::Environment::get().getMechanicsManager()->getActorsFighting(getPlayer()).size() != 0; } @@ -286,6 +297,7 @@ namespace MWWorld mForwardBackward = 0; mTeleported = false; mAttackingOrSpell = false; + mJumping = false; mCurrentCrimeId = -1; mPaidCrimeId = -1; mPreviousItems.clear(); diff --git a/apps/openmw/mwworld/player.hpp b/apps/openmw/mwworld/player.hpp index d4a9f6050..4b92fa396 100644 --- a/apps/openmw/mwworld/player.hpp +++ b/apps/openmw/mwworld/player.hpp @@ -56,6 +56,7 @@ namespace MWWorld MWMechanics::AttributeValue mSaveAttributes[ESM::Attribute::Length]; bool mAttackingOrSpell; + bool mJumping; public: @@ -111,6 +112,9 @@ namespace MWWorld void setAttackingOrSpell(bool attackingOrSpell); bool getAttackingOrSpell() const; + void setJumping(bool jumping); + bool getJumping() const; + ///Checks all nearby actors to see if anyone has an aipackage against you bool isInCombat(); From 09427d3f5ec05aaa00f31da272f725f1cdba6976 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sun, 16 Sep 2018 14:38:58 +0400 Subject: [PATCH 043/163] Take in account transformations of NiTriShape and NiSkinData in skinning (bug #4437) --- CHANGELOG.md | 1 + components/nifosg/nifloader.cpp | 6 +++++- components/sceneutil/riggeometry.cpp | 9 +++++++++ components/sceneutil/riggeometry.hpp | 2 ++ 4 files changed, 17 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6bea04fcb..9e8f04092 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -67,6 +67,7 @@ Bug #4431: "Lock 0" console command is a no-op Bug #4432: Guards behaviour is incorrect if they do not have AI packages Bug #4433: Guard behaviour is incorrect with Alarm = 0 + Bug #4437: Transformations for NiSkinInstance are ignored Bug #4451: Script fails to compile when using "Begin, [ScriptName]" syntax Bug #4452: Default terrain texture bleeds through texture transitions Bug #4453: Quick keys behaviour is invalid for equipment diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 3198e995c..81898e477 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -1082,7 +1082,10 @@ namespace NifOsg // Assign bone weights osg::ref_ptr map (new SceneUtil::RigGeometry::InfluenceMap); + // We should take in account transformation of NiTriShape and NiSkinData const Nif::NiSkinData *data = skin->data.getPtr(); + osg::Matrixf shapeTransforms = triShape->trafo.toMatrix() * data->trafo.toMatrix(); + const Nif::NodeList &bones = skin->bones; for(size_t i = 0;i < bones.length();i++) { @@ -1096,11 +1099,12 @@ namespace NifOsg influence.mWeights.insert(indexWeight); } influence.mInvBindMatrix = data->bones[i].trafo.toMatrix(); - influence.mBoundSphere = osg::BoundingSpheref(data->bones[i].boundSphereCenter, data->bones[i].boundSphereRadius); + influence.mBoundSphere = osg::BoundingSpheref(shapeTransforms * data->bones[i].boundSphereCenter, data->bones[i].boundSphereRadius); map->mMap.insert(std::make_pair(boneName, influence)); } rig->setInfluenceMap(map); + rig->setRigTransforms(*&shapeTransforms); parentNode->addChild(rig); } diff --git a/components/sceneutil/riggeometry.cpp b/components/sceneutil/riggeometry.cpp index c409bcd5c..94e4a3b3e 100644 --- a/components/sceneutil/riggeometry.cpp +++ b/components/sceneutil/riggeometry.cpp @@ -49,6 +49,7 @@ RigGeometry::RigGeometry(const RigGeometry ©, const osg::CopyOp ©op) : Drawable(copy, copyop) , mSkeleton(NULL) , mInfluenceMap(copy.mInfluenceMap) + , mRigTransforms(copy.mRigTransforms) , mLastFrameNumber(0) , mBoundsFirstFrame(true) { @@ -214,6 +215,9 @@ void RigGeometry::cull(osg::NodeVisitor* nv) if (mGeomToSkelMatrix) resultMat *= (*mGeomToSkelMatrix); + if (!mRigTransforms.isIdentity()) + resultMat *= mRigTransforms; + for (auto &vertex : pair.second) { (*positionDst)[vertex] = resultMat.preMult((*positionSrc)[vertex]); @@ -309,6 +313,11 @@ void RigGeometry::setInfluenceMap(osg::ref_ptr influenceMap) mInfluenceMap = influenceMap; } +void RigGeometry::setRigTransforms(osg::Matrixf& transform) +{ + mRigTransforms = transform; +} + void RigGeometry::accept(osg::NodeVisitor &nv) { if (!nv.validNodeMask(*this)) diff --git a/components/sceneutil/riggeometry.hpp b/components/sceneutil/riggeometry.hpp index 60b3edc9d..723455234 100644 --- a/components/sceneutil/riggeometry.hpp +++ b/components/sceneutil/riggeometry.hpp @@ -40,6 +40,7 @@ namespace SceneUtil }; void setInfluenceMap(osg::ref_ptr influenceMap); + void setRigTransforms(osg::Matrixf& transform); /// Initialize this geometry from the source geometry. /// @note The source geometry will not be modified. @@ -65,6 +66,7 @@ namespace SceneUtil osg::ref_ptr mGeomToSkelMatrix; osg::ref_ptr mInfluenceMap; + osg::Matrixf mRigTransforms; typedef std::pair BoneBindMatrixPair; From 95aa05e41be646c7ce556789f4c75619cfcfa6b6 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 16 Sep 2018 16:47:33 +0300 Subject: [PATCH 044/163] Tweaks to script parser messages and pathgrid warnings --- apps/opencs/model/tools/pathgridcheck.cpp | 18 ++++++++---------- apps/opencs/model/tools/scriptcheck.cpp | 2 +- components/compiler/declarationparser.cpp | 2 +- components/compiler/exprparser.cpp | 8 ++++---- components/compiler/junkparser.cpp | 4 ++-- components/compiler/lineparser.cpp | 18 +++++++++--------- 6 files changed, 25 insertions(+), 27 deletions(-) diff --git a/apps/opencs/model/tools/pathgridcheck.cpp b/apps/opencs/model/tools/pathgridcheck.cpp index 88750ad7f..febb79c64 100644 --- a/apps/opencs/model/tools/pathgridcheck.cpp +++ b/apps/opencs/model/tools/pathgridcheck.cpp @@ -56,8 +56,7 @@ void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& message if (pointList[pathgrid.mEdges[i].mV0].mOtherIndex[j] == pathgrid.mEdges[i].mV1) { std::ostringstream ss; - ss << "Duplicate edge between points" - << pathgrid.mEdges[i].mV0 << " and " << pathgrid.mEdges[i].mV1; + ss << "Duplicate edge between points #" << pathgrid.mEdges[i].mV0 << " and #" << pathgrid.mEdges[i].mV1; messages.add (id, ss.str(), "", CSMDoc::Message::Severity_Error); break; } @@ -70,7 +69,7 @@ void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& message else { std::ostringstream ss; - ss << "An edge is connected to a non-existent point " << pathgrid.mEdges[i].mV0; + ss << "An edge is connected to a non-existent point #" << pathgrid.mEdges[i].mV0; messages.add (id, ss.str(), "", CSMDoc::Message::Severity_Error); } } @@ -93,7 +92,7 @@ void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& message if (!foundReverse) { std::ostringstream ss; - ss << "Missing edge between points " << i << " and " << pointList[i].mOtherIndex[j]; + ss << "Missing edge between points #" << i << " and #" << pointList[i].mOtherIndex[j]; messages.add (id, ss.str(), "", CSMDoc::Message::Severity_Error); } } @@ -110,7 +109,7 @@ void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& message if (it == duplList.end()) { std::ostringstream ss; - ss << "Point " << i << " duplicates point " << j + ss << "Point #" << i << " duplicates point #" << j << " (" << pathgrid.mPoints[i].mX << ", " << pathgrid.mPoints[i].mY << ", " << pathgrid.mPoints[i].mZ << ")"; messages.add (id, ss.str(), "", CSMDoc::Message::Severity_Warning); @@ -127,11 +126,10 @@ void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& message if (pointList[i].mConnectionNum == 0) { std::ostringstream ss; - ss << "Point " << i << " (" - << pathgrid.mPoints[i].mX << ", " - << pathgrid.mPoints[i].mY << ", " - << pathgrid.mPoints[i].mZ << ") " - << "is disconnected from other points"; + ss << "Point #" << i << " (" + << pathgrid.mPoints[i].mX << ", " + << pathgrid.mPoints[i].mY << ", " + << pathgrid.mPoints[i].mZ << ") is disconnected from other points"; messages.add (id, ss.str(), "", CSMDoc::Message::Severity_Warning); } } diff --git a/apps/opencs/model/tools/scriptcheck.cpp b/apps/opencs/model/tools/scriptcheck.cpp index e62f9eb88..d3d9d1503 100644 --- a/apps/opencs/model/tools/scriptcheck.cpp +++ b/apps/opencs/model/tools/scriptcheck.cpp @@ -30,7 +30,7 @@ void CSMTools::ScriptCheckStage::report (const std::string& message, const Compi CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Script, mId); - stream << "Line " << loc.mLine << ", column " << loc.mColumn << " (" << loc.mLiteral << "): " << message; + stream << "line " << loc.mLine << ", column " << loc.mColumn << ": " << message << " (" << loc.mLiteral << ")"; std::ostringstream hintStream; diff --git a/components/compiler/declarationparser.cpp b/components/compiler/declarationparser.cpp index ffac252d5..e85c8c3ec 100644 --- a/components/compiler/declarationparser.cpp +++ b/components/compiler/declarationparser.cpp @@ -24,7 +24,7 @@ bool Compiler::DeclarationParser::parseName (const std::string& name, const Toke if (type!=' ') { /// \todo add option to make re-declared local variables an error - getErrorHandler().warning ("can't re-declare local variable (ignoring declaration)", + getErrorHandler().warning ("ignoring local variable re-declaration", loc); mState = State_End; diff --git a/components/compiler/exprparser.cpp b/components/compiler/exprparser.cpp index 7cb0abfd1..6b849ec3a 100644 --- a/components/compiler/exprparser.cpp +++ b/components/compiler/exprparser.cpp @@ -422,7 +422,7 @@ namespace Compiler { if (!hasExplicit) { - getErrorHandler().warning ("stray explicit reference (ignoring it)", loc); + getErrorHandler().warning ("ignoring stray explicit reference", loc); mExplicit.clear(); } @@ -791,7 +791,7 @@ namespace Compiler ++optionalCount; } else - getErrorHandler().warning ("Ignoring extra argument", + getErrorHandler().warning ("ignoring extra argument", stringParser.getTokenLoc()); } else if (*iter=='X') @@ -805,7 +805,7 @@ namespace Compiler if (parser.isEmpty()) break; else - getErrorHandler().warning("Ignoring extra argument", parser.getTokenLoc()); + getErrorHandler().warning("ignoring extra argument", parser.getTokenLoc()); } else if (*iter=='z') { @@ -817,7 +817,7 @@ namespace Compiler if (discardParser.isEmpty()) break; else - getErrorHandler().warning("Ignoring extra argument", discardParser.getTokenLoc()); + getErrorHandler().warning("ignoring extra argument", discardParser.getTokenLoc()); } else if (*iter=='j') { diff --git a/components/compiler/junkparser.cpp b/components/compiler/junkparser.cpp index 7608e9bab..5910cca43 100644 --- a/components/compiler/junkparser.cpp +++ b/components/compiler/junkparser.cpp @@ -29,7 +29,7 @@ bool Compiler::JunkParser::parseName (const std::string& name, const TokenLoc& l bool Compiler::JunkParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner) { if (keyword==mIgnoreKeyword) - reportWarning ("found junk (ignoring it)", loc); + reportWarning ("ignoring found junk", loc); else scanner.putbackKeyword (keyword, loc); @@ -39,7 +39,7 @@ bool Compiler::JunkParser::parseKeyword (int keyword, const TokenLoc& loc, Scann bool Compiler::JunkParser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner) { if (code==Scanner::S_member) - reportWarning ("found junk (ignoring it)", loc); + reportWarning ("ignoring found junk", loc); else scanner.putbackSpecial (code, loc); diff --git a/components/compiler/lineparser.cpp b/components/compiler/lineparser.cpp index c2c1dff9b..3f9d2e790 100644 --- a/components/compiler/lineparser.cpp +++ b/components/compiler/lineparser.cpp @@ -88,7 +88,7 @@ namespace Compiler { if (mState==PotentialEndState) { - getErrorHandler().warning ("stray string argument (ignoring it)", loc); + getErrorHandler().warning ("ignoring stray string argument", loc); mState = EndState; return true; } @@ -233,7 +233,7 @@ namespace Compiler if (mState==SetPotentialMemberVarState && keyword==Scanner::K_to) { - getErrorHandler().warning ("unknown variable (ignoring set instruction)", loc); + getErrorHandler().warning ("unknown variable, ignoring set instruction", loc); SkipParser skip (getErrorHandler(), getContext()); scanner.scan (skip); return false; @@ -286,7 +286,7 @@ namespace Compiler { if (!hasExplicit && mState==ExplicitState) { - getErrorHandler().warning ("stray explicit reference (ignoring it)", loc); + getErrorHandler().warning ("ignoring stray explicit reference", loc); mExplicit.clear(); } @@ -344,7 +344,7 @@ namespace Compiler { if (!hasExplicit && !mExplicit.empty()) { - getErrorHandler().warning ("stray explicit reference (ignoring it)", loc); + getErrorHandler().warning ("ignoring stray explicit reference", loc); mExplicit.clear(); } @@ -360,7 +360,7 @@ namespace Compiler if (mState==ExplicitState) { // drop stray explicit reference - getErrorHandler().warning ("stray explicit reference (ignoring it)", loc); + getErrorHandler().warning ("ignoring stray explicit reference", loc); mState = BeginState; mExplicit.clear(); } @@ -412,19 +412,19 @@ namespace Compiler case Scanner::K_else: - getErrorHandler().warning ("stray else (ignoring it)", loc); + getErrorHandler().warning ("ignoring stray else", loc); mState = EndState; return true; case Scanner::K_endif: - getErrorHandler().warning ("stray endif (ignoring it)", loc); + getErrorHandler().warning ("ignoring stray endif", loc); mState = EndState; return true; case Scanner::K_begin: - getErrorHandler().warning ("stray begin (ignoring it)", loc); + getErrorHandler().warning ("ignoring stray begin", loc); mState = EndState; return true; } @@ -491,7 +491,7 @@ namespace Compiler { if (mState==EndState && code==Scanner::S_open) { - getErrorHandler().warning ("stray '[' or '(' at the end of the line (ignoring it)", + getErrorHandler().warning ("ignoring stray '[' or '(' at the end of the line", loc); return true; } From 2f054cc7a8f1f2c8196522419cb32a985f4ad653 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 16 Sep 2018 17:09:47 +0300 Subject: [PATCH 045/163] Update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6bea04fcb..af29a67e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -121,6 +121,7 @@ Bug #4628: NPC record reputation, disposition and faction rank should have unsigned char type Bug #4633: Sneaking stance affects speed even if the actor is not able to crouch Feature #912: Editor: Add missing icons to UniversalId tables + Feature #1617: Editor: Enchantment effect record verifier Feature #1645: Casting effects from objects Feature #2606: Editor: Implemented (optional) case sensitive global search Feature #3083: Play animation when NPC is casting spell via script @@ -155,6 +156,7 @@ Task #4606: Support Rapture3D's OpenAL driver Task #4613: Incomplete type errors when compiling with g++ on OSX 10.9 Task #4621: Optimize combat AI + Task #4643: Revise editor record verifying functionality 0.44.0 ------ From 2961f0d8109bb47d5ce3434988b0dcc97637f9b1 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sun, 16 Sep 2018 20:47:51 +0400 Subject: [PATCH 046/163] Allow to use the %Name for creatures (bug #4644) --- CHANGELOG.md | 1 + apps/openmw/mwscript/interpretercontext.cpp | 13 ++++++++++--- apps/openmw/mwscript/interpretercontext.hpp | 2 +- components/interpreter/context.hpp | 2 +- components/interpreter/defines.cpp | 2 +- 5 files changed, 14 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6bea04fcb..f37c5c4c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -120,6 +120,7 @@ Bug #4622: Recharging enchanted items with Soul Gems does not award experience if it fails Bug #4628: NPC record reputation, disposition and faction rank should have unsigned char type Bug #4633: Sneaking stance affects speed even if the actor is not able to crouch + Bug #4644: %Name should be available for all actors, not just for NPCs Feature #912: Editor: Add missing icons to UniversalId tables Feature #1645: Casting effects from objects Feature #2606: Editor: Implemented (optional) case sensitive global search diff --git a/apps/openmw/mwscript/interpretercontext.cpp b/apps/openmw/mwscript/interpretercontext.cpp index f4e729da1..07b7a3bff 100644 --- a/apps/openmw/mwscript/interpretercontext.cpp +++ b/apps/openmw/mwscript/interpretercontext.cpp @@ -288,10 +288,17 @@ namespace MWScript return "None"; } - std::string InterpreterContext::getNPCName() const + std::string InterpreterContext::getActorName() const { - ESM::NPC npc = *getReferenceImp().get()->mBase; - return npc.mName; + const MWWorld::Ptr& ptr = getReferenceImp(); + if (ptr.getClass().isNpc()) + { + const ESM::NPC* npc = ptr.get()->mBase; + return npc->mName; + } + + const ESM::Creature* creature = ptr.get()->mBase; + return creature->mName; } std::string InterpreterContext::getNPCRace() const diff --git a/apps/openmw/mwscript/interpretercontext.hpp b/apps/openmw/mwscript/interpretercontext.hpp index ac8e1833b..5f1f4f7ab 100644 --- a/apps/openmw/mwscript/interpretercontext.hpp +++ b/apps/openmw/mwscript/interpretercontext.hpp @@ -93,7 +93,7 @@ namespace MWScript virtual std::string getActionBinding(const std::string& action) const; - virtual std::string getNPCName() const; + virtual std::string getActorName() const; virtual std::string getNPCRace() const; diff --git a/components/interpreter/context.hpp b/components/interpreter/context.hpp index 881687366..4c320879e 100644 --- a/components/interpreter/context.hpp +++ b/components/interpreter/context.hpp @@ -55,7 +55,7 @@ namespace Interpreter virtual std::string getActionBinding(const std::string& action) const = 0; - virtual std::string getNPCName() const = 0; + virtual std::string getActorName() const = 0; virtual std::string getNPCRace() const = 0; diff --git a/components/interpreter/defines.cpp b/components/interpreter/defines.cpp index 3c6226d9c..0ceed80d5 100644 --- a/components/interpreter/defines.cpp +++ b/components/interpreter/defines.cpp @@ -138,7 +138,7 @@ namespace Interpreter{ retval << context.getNPCRace(); } else if((found = check(temp, "name", &i, &start))){ - retval << context.getNPCName(); + retval << context.getActorName(); } } else { // In messagebox or book, not dialogue From 9c8fc0557a492b80dec644b07a9b1af101622fea Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Mon, 17 Sep 2018 13:35:22 +0400 Subject: [PATCH 047/163] Fix MSVC warning about possibly uninitialized movestate variable --- apps/openmw/mwmechanics/character.cpp | 47 ++++++++++++++------------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 123f7bf59..85e55ec38 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -415,37 +415,38 @@ void CharacterController::refreshJumpAnims(const WeaponInfo* weap, JumpingState void CharacterController::refreshMovementAnims(const WeaponInfo* weap, CharacterState movement, CharacterState& idle, bool force) { + if (movement == mMovementState && idle == mIdleState && !force) + return; + std::string movementAnimName; MWRender::Animation::BlendMask movemask; const StateInfo *movestate; - if(force || movement != mMovementState || idle != mIdleState) + + movemask = MWRender::Animation::BlendMask_All; + movestate = std::find_if(sMovementList, sMovementListEnd, FindCharState(movement)); + if(movestate != sMovementListEnd) { - movemask = MWRender::Animation::BlendMask_All; - movestate = std::find_if(sMovementList, sMovementListEnd, FindCharState(movement)); - if(movestate != sMovementListEnd) + movementAnimName = movestate->groupname; + if(weap != sWeaponTypeListEnd && movementAnimName.find("swim") == std::string::npos) { - movementAnimName = movestate->groupname; - if(weap != sWeaponTypeListEnd && movementAnimName.find("swim") == std::string::npos) + if (mWeaponType == WeapType_Spell && (movement == CharState_TurnLeft || movement == CharState_TurnRight)) // Spellcasting stance turning is a special case + movementAnimName = weap->shortgroup + movementAnimName; + else + movementAnimName += weap->shortgroup; + + if(!mAnimation->hasAnimation(movementAnimName)) { - if (mWeaponType == WeapType_Spell && (movement == CharState_TurnLeft || movement == CharState_TurnRight)) // Spellcasting stance turning is a special case - movementAnimName = weap->shortgroup + movementAnimName; - else - movementAnimName += weap->shortgroup; + movemask = MWRender::Animation::BlendMask_LowerBody; + movementAnimName = movestate->groupname; - if(!mAnimation->hasAnimation(movementAnimName)) - { - movemask = MWRender::Animation::BlendMask_LowerBody; - movementAnimName = movestate->groupname; + // Since we apply movement only for lower body, do not reset idle animations. + // For upper body there will be idle animation. + if (idle == CharState_None) + idle = CharState_Idle; - // Since we apply movement only for lower body, do not reset idle animations. - // For upper body there will be idle animation. - if (idle == CharState_None) - idle = CharState_Idle; - - // For crossbow animations use 1h ones as fallback - if (mWeaponType == WeapType_Crossbow) - movementAnimName += "1h"; - } + // For crossbow animations use 1h ones as fallback + if (mWeaponType == WeapType_Crossbow) + movementAnimName += "1h"; } } } From 70ed8fd1a97b93f360cb841cd1f276da0f632c42 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Mon, 17 Sep 2018 14:52:43 +0400 Subject: [PATCH 048/163] Use constants instead of widely used magic numbers (task #4645) --- CHANGELOG.md | 1 + apps/essimporter/converter.cpp | 10 ++++--- apps/essimporter/convertplayer.cpp | 6 ++--- apps/essimporter/importer.cpp | 7 ++--- apps/opencs/model/world/cellcoordinates.cpp | 6 ++--- apps/opencs/model/world/commands.cpp | 6 ++--- apps/opencs/view/render/cellarrow.cpp | 4 ++- apps/opencs/view/render/cellmarker.cpp | 4 ++- apps/opencs/view/render/instancemode.cpp | 4 +-- apps/opencs/view/render/object.cpp | 6 ++--- .../view/render/pagedworldspacewidget.cpp | 20 ++++++-------- apps/openmw/mwclass/door.cpp | 4 +-- apps/openmw/mwclass/npc.cpp | 3 ++- apps/openmw/mwclass/weapon.cpp | 5 ++-- apps/openmw/mwgui/mapwindow.cpp | 2 +- apps/openmw/mwgui/race.cpp | 2 +- apps/openmw/mwmechanics/aiavoiddoor.cpp | 6 ++--- apps/openmw/mwmechanics/aiwander.cpp | 3 +-- apps/openmw/mwphysics/physicssystem.cpp | 3 ++- apps/openmw/mwrender/globalmap.cpp | 4 +-- apps/openmw/mwrender/localmap.cpp | 3 ++- apps/openmw/mwrender/water.cpp | 7 +++-- apps/openmw/mwrender/water.hpp | 2 -- apps/openmw/mwsound/openal_output.cpp | 14 ++++------ apps/openmw/mwworld/projectilemanager.cpp | 4 ++- apps/openmw/mwworld/scene.cpp | 4 +-- apps/openmw/mwworld/worldimp.cpp | 17 ++++++------ components/CMakeLists.txt | 2 +- components/esm/loadland.hpp | 4 ++- components/esmterrain/storage.cpp | 22 +++++++-------- components/misc/constants.hpp | 27 +++++++++++++++++++ components/terrain/quadtreeworld.cpp | 4 ++- 32 files changed, 123 insertions(+), 93 deletions(-) create mode 100644 components/misc/constants.hpp diff --git a/CHANGELOG.md b/CHANGELOG.md index 1fb9837bd..9e2a71341 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -162,6 +162,7 @@ Task #4613: Incomplete type errors when compiling with g++ on OSX 10.9 Task #4621: Optimize combat AI Task #4643: Revise editor record verifying functionality + Task #4645: Use constants instead of widely used magic numbers 0.44.0 ------ diff --git a/apps/essimporter/converter.cpp b/apps/essimporter/converter.cpp index 2473daf95..900fbb05c 100644 --- a/apps/essimporter/converter.cpp +++ b/apps/essimporter/converter.cpp @@ -8,6 +8,8 @@ #include #include +#include + #include "convertcrec.hpp" #include "convertcntc.hpp" #include "convertscri.hpp" @@ -288,12 +290,12 @@ namespace ESSImport notepos[1] += 31.f; notepos[0] += 0.5; notepos[1] += 0.5; - notepos[0] = 8192 * notepos[0] / 32.f; - notepos[1] = 8192 * notepos[1] / 32.f; + notepos[0] = Constants::CellSizeInUnits * notepos[0] / 32.f; + notepos[1] = Constants::CellSizeInUnits * notepos[1] / 32.f; if (cell.isExterior()) { - notepos[0] += 8192 * cell.mData.mX; - notepos[1] += 8192 * cell.mData.mY; + notepos[0] += Constants::CellSizeInUnits * cell.mData.mX; + notepos[1] += Constants::CellSizeInUnits * cell.mData.mY; } // TODO: what encoding is this in? std::string note = esm.getHNString("MPNT"); diff --git a/apps/essimporter/convertplayer.cpp b/apps/essimporter/convertplayer.cpp index 4a4a9a573..5e2da2b03 100644 --- a/apps/essimporter/convertplayer.cpp +++ b/apps/essimporter/convertplayer.cpp @@ -1,5 +1,6 @@ #include "convertplayer.hpp" +#include #include namespace ESSImport @@ -78,9 +79,8 @@ namespace ESSImport if (pcdt.mHasENAM) { - const int cellSize = 8192; - out.mLastKnownExteriorPosition[0] = (pcdt.mENAM.mCellX + 0.5f) * cellSize; - out.mLastKnownExteriorPosition[1] = (pcdt.mENAM.mCellY + 0.5f) * cellSize; + out.mLastKnownExteriorPosition[0] = (pcdt.mENAM.mCellX + 0.5f) * Constants::CellSizeInUnits; + out.mLastKnownExteriorPosition[1] = (pcdt.mENAM.mCellY + 0.5f) * Constants::CellSizeInUnits; out.mLastKnownExteriorPosition[2] = 0.0f; } } diff --git a/apps/essimporter/importer.cpp b/apps/essimporter/importer.cpp index 4538d4e63..a54c13334 100644 --- a/apps/essimporter/importer.cpp +++ b/apps/essimporter/importer.cpp @@ -25,6 +25,8 @@ #include #include +#include + #include #include "importercontext.hpp" @@ -413,9 +415,8 @@ namespace ESSImport if (context.mPlayer.mCellId.mPaged) { // exterior cell -> determine cell coordinates based on position - const int cellSize = 8192; - int cellX = static_cast(std::floor(context.mPlayer.mObject.mPosition.pos[0]/cellSize)); - int cellY = static_cast(std::floor(context.mPlayer.mObject.mPosition.pos[1] / cellSize)); + int cellX = static_cast(std::floor(context.mPlayer.mObject.mPosition.pos[0] / Constants::CellSizeInUnits)); + int cellY = static_cast(std::floor(context.mPlayer.mObject.mPosition.pos[1] / Constants::CellSizeInUnits)); context.mPlayer.mCellId.mIndex.mX = cellX; context.mPlayer.mCellId.mIndex.mY = cellY; } diff --git a/apps/opencs/model/world/cellcoordinates.cpp b/apps/opencs/model/world/cellcoordinates.cpp index abb3bc82e..dbe90b906 100644 --- a/apps/opencs/model/world/cellcoordinates.cpp +++ b/apps/opencs/model/world/cellcoordinates.cpp @@ -5,6 +5,8 @@ #include #include +#include + CSMWorld::CellCoordinates::CellCoordinates() : mX (0), mY (0) {} CSMWorld::CellCoordinates::CellCoordinates (int x, int y) : mX (x), mY (y) {} @@ -61,9 +63,7 @@ std::pair CSMWorld::CellCoordinates::fromId ( std::pair CSMWorld::CellCoordinates::coordinatesToCellIndex (float x, float y) { - const int cellSize = 8192; - - return std::make_pair (std::floor (x/cellSize), std::floor (y/cellSize)); + return std::make_pair (std::floor (x / Constants::CellSizeInUnits), std::floor (y / Constants::CellSizeInUnits)); } bool CSMWorld::operator== (const CellCoordinates& left, const CellCoordinates& right) diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index 79900c6c4..4133b2050 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -445,16 +445,14 @@ void CSMWorld::UpdateCellCommand::redo() int cellColumn = mModel.searchColumnIndex (Columns::ColumnId_Cell); mIndex = mModel.index (mRow, cellColumn); - const int cellSize = 8192; - QModelIndex xIndex = mModel.index ( mRow, mModel.findColumnIndex (Columns::ColumnId_PositionXPos)); QModelIndex yIndex = mModel.index ( mRow, mModel.findColumnIndex (Columns::ColumnId_PositionYPos)); - int x = std::floor (mModel.data (xIndex).toFloat() / cellSize); - int y = std::floor (mModel.data (yIndex).toFloat() / cellSize); + int x = std::floor (mModel.data (xIndex).toFloat() / Constants::CellSizeInUnits); + int y = std::floor (mModel.data (yIndex).toFloat() / Constants::CellSizeInUnits); std::ostringstream stream; diff --git a/apps/opencs/view/render/cellarrow.cpp b/apps/opencs/view/render/cellarrow.cpp index b8c89c83d..b6fee1545 100644 --- a/apps/opencs/view/render/cellarrow.cpp +++ b/apps/opencs/view/render/cellarrow.cpp @@ -10,6 +10,8 @@ #include "../../model/prefs/state.hpp" #include "../../model/prefs/shortcutmanager.hpp" +#include + #include "mask.hpp" CSVRender::CellArrowTag::CellArrowTag (CellArrow *arrow) @@ -57,7 +59,7 @@ QString CSVRender::CellArrowTag::getToolTip (bool hideBasics) const void CSVRender::CellArrow::adjustTransform() { // position - const int cellSize = 8192; + const int cellSize = Constants::CellSizeInUnits; const int offset = cellSize / 2 + 800; int x = mCoordinates.getX()*cellSize + cellSize/2; diff --git a/apps/opencs/view/render/cellmarker.cpp b/apps/opencs/view/render/cellmarker.cpp index d0521a7b7..3de96ab02 100644 --- a/apps/opencs/view/render/cellmarker.cpp +++ b/apps/opencs/view/render/cellmarker.cpp @@ -5,6 +5,8 @@ #include #include +#include + CSVRender::CellMarkerTag::CellMarkerTag(CellMarker *marker) : TagBase(Mask_CellMarker), mMarker(marker) {} @@ -49,7 +51,7 @@ void CSVRender::CellMarker::buildMarker() void CSVRender::CellMarker::positionMarker() { - const int cellSize = 8192; + const int cellSize = Constants::CellSizeInUnits; const int markerHeight = 0; // Move marker to center of cell. diff --git a/apps/opencs/view/render/instancemode.cpp b/apps/opencs/view/render/instancemode.cpp index 4b14e29bf..1cf8a5698 100644 --- a/apps/opencs/view/render/instancemode.cpp +++ b/apps/opencs/view/render/instancemode.cpp @@ -29,15 +29,13 @@ int CSVRender::InstanceMode::getSubModeFromId (const std::string& id) const osg::Vec3f CSVRender::InstanceMode::quatToEuler(const osg::Quat& rot) const { - const float Pi = 3.14159265f; - float x, y, z; float test = 2 * (rot.w() * rot.y() + rot.x() * rot.z()); if (std::abs(test) >= 1.f) { x = atan2(rot.x(), rot.w()); - y = (test > 0) ? (Pi / 2) : (-Pi / 2); + y = (test > 0) ? (osg::PI / 2) : (-osg::PI / 2); z = 0; } else diff --git a/apps/opencs/view/render/object.cpp b/apps/opencs/view/render/object.cpp index 8301f4e9e..2b1e3adde 100644 --- a/apps/opencs/view/render/object.cpp +++ b/apps/opencs/view/render/object.cpp @@ -313,20 +313,18 @@ osg::ref_ptr CSVRender::Object::makeMoveOrScaleMarker (int axis) osg::ref_ptr CSVRender::Object::makeRotateMarker (int axis) { - const float Pi = 3.14159265f; - const float InnerRadius = std::max(MarkerShaftBaseLength, mBaseNode->getBound().radius()); const float OuterRadius = InnerRadius + MarkerShaftWidth; const float SegmentDistance = 100.f; - const size_t SegmentCount = std::min(64, std::max(24, (int)(OuterRadius * 2 * Pi / SegmentDistance))); + const size_t SegmentCount = std::min(64, std::max(24, (int)(OuterRadius * 2 * osg::PI / SegmentDistance))); const size_t VerticesPerSegment = 4; const size_t IndicesPerSegment = 24; const size_t VertexCount = SegmentCount * VerticesPerSegment; const size_t IndexCount = SegmentCount * IndicesPerSegment; - const float Angle = 2 * Pi / SegmentCount; + const float Angle = 2 * osg::PI / SegmentCount; const unsigned short IndexPattern[IndicesPerSegment] = { diff --git a/apps/opencs/view/render/pagedworldspacewidget.cpp b/apps/opencs/view/render/pagedworldspacewidget.cpp index 1d1a7cd17..ccea70761 100644 --- a/apps/opencs/view/render/pagedworldspacewidget.cpp +++ b/apps/opencs/view/render/pagedworldspacewidget.cpp @@ -8,6 +8,8 @@ #include +#include + #include "../../model/prefs/shortcut.hpp" #include "../../model/world/tablemimedata.hpp" @@ -506,13 +508,11 @@ void CSVRender::PagedWorldspaceWidget::moveCellSelection (int x, int y) void CSVRender::PagedWorldspaceWidget::addCellToSceneFromCamera (int offsetX, int offsetY) { - const int CellSize = 8192; - osg::Vec3f eye, center, up; getCamera()->getViewMatrixAsLookAt(eye, center, up); - int cellX = (int)std::floor(center.x() / CellSize) + offsetX; - int cellY = (int)std::floor(center.y() / CellSize) + offsetY; + int cellX = (int)std::floor(center.x() / Constants::CellSizeInUnits) + offsetX; + int cellY = (int)std::floor(center.y() / Constants::CellSizeInUnits) + offsetY; CSMWorld::CellCoordinates cellCoordinates(cellX, cellY); @@ -738,22 +738,18 @@ void CSVRender::PagedWorldspaceWidget::selectAllWithSameParentId (int elementMas std::string CSVRender::PagedWorldspaceWidget::getCellId (const osg::Vec3f& point) const { - const int cellSize = 8192; - CSMWorld::CellCoordinates cellCoordinates ( - static_cast (std::floor (point.x()/cellSize)), - static_cast (std::floor (point.y()/cellSize))); + static_cast (std::floor (point.x() / Constants::CellSizeInUnits)), + static_cast (std::floor (point.y() / Constants::CellSizeInUnits))); return cellCoordinates.getId (mWorldspace); } CSVRender::Cell* CSVRender::PagedWorldspaceWidget::getCell(const osg::Vec3d& point) const { - const int cellSize = 8192; - CSMWorld::CellCoordinates coords( - static_cast (std::floor (point.x()/cellSize)), - static_cast (std::floor (point.y()/cellSize))); + static_cast (std::floor (point.x() / Constants::CellSizeInUnits)), + static_cast (std::floor (point.y() / Constants::CellSizeInUnits))); std::map::const_iterator searchResult = mCells.find(coords); if (searchResult != mCells.end()) diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index d738974dd..a26118029 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -213,7 +213,7 @@ namespace MWClass closeSound, 0.5f); // Doors rotate at 90 degrees per second, so start the sound at // where it would be at the current rotation. - float offset = doorRot/(3.14159265f * 0.5f); + float offset = doorRot/(osg::PI * 0.5f); action->setSoundOffset(offset); action->setSound(openSound); } @@ -221,7 +221,7 @@ namespace MWClass { MWBase::Environment::get().getSoundManager()->fadeOutSound3D(ptr, openSound, 0.5f); - float offset = 1.0f - doorRot/(3.14159265f * 0.5f); + float offset = 1.0f - doorRot/(osg::PI * 0.5f); action->setSoundOffset(std::max(offset, 0.0f)); action->setSound(closeSound); } diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 3d019ea90..4979db2fd 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -2,6 +2,7 @@ #include +#include #include #include @@ -1020,7 +1021,7 @@ namespace MWClass if(stats.getStance(MWMechanics::CreatureStats::Stance_Run)) x *= gmst.fJumpRunMultiplier->mValue.getFloat(); x *= npcdata->mNpcStats.getFatigueTerm(); - x -= -627.2f;/*gravity constant*/ + x -= -Constants::GravityConst * Constants::UnitsPerMeter; x /= 3.0f; return x; diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index 466ae4716..5e0f989a5 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -1,6 +1,7 @@ #include "weapon.hpp" #include +#include #include #include "../mwbase/environment.hpp" @@ -328,9 +329,9 @@ namespace MWClass // add reach and attack speed for melee weapon if (ref->mBase->mData.mType < 9 && Settings::Manager::getBool("show melee info", "Game")) { - // 64 game units = 1 yard = 3 ft, display value in feet + // display value in feet const float combatDistance = store.get().find("fCombatDistance")->mValue.getFloat() * ref->mBase->mData.mReach; - text += MWGui::ToolTips::getWeightString(combatDistance*3/64, "#{sRange}"); + text += MWGui::ToolTips::getWeightString(combatDistance / Constants::UnitsPerFoot, "#{sRange}"); text += " #{sFeet}"; text += MWGui::ToolTips::getPercentString(ref->mBase->mData.mSpeed, "#{sAttributeSpeed}"); diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index c1ff9510d..2c8ad0565 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -33,7 +33,7 @@ namespace { - const int cellSize = 8192; + const int cellSize = Constants::CellSizeInUnits; enum LocalMapWidgetDepth { diff --git a/apps/openmw/mwgui/race.cpp b/apps/openmw/mwgui/race.cpp index 96c0d7de4..ee058e02e 100644 --- a/apps/openmw/mwgui/race.cpp +++ b/apps/openmw/mwgui/race.cpp @@ -212,7 +212,7 @@ namespace MWGui void RaceDialog::onHeadRotate(MyGUI::ScrollBar* scroll, size_t _position) { - float angle = (float(_position) / (scroll->getScrollRange()-1) - 0.5f) * 3.14f * 2; + float angle = (float(_position) / (scroll->getScrollRange()-1) - 0.5f) * osg::PI * 2; mPreview->setAngle (angle); mCurrentAngle = angle; diff --git a/apps/openmw/mwmechanics/aiavoiddoor.cpp b/apps/openmw/mwmechanics/aiavoiddoor.cpp index bb03ff53b..e7d1ecee1 100644 --- a/apps/openmw/mwmechanics/aiavoiddoor.cpp +++ b/apps/openmw/mwmechanics/aiavoiddoor.cpp @@ -33,9 +33,9 @@ bool MWMechanics::AiAvoidDoor::execute (const MWWorld::Ptr& actor, CharacterCont float distance = x * x + y * y + z * z; if(distance < 10 * 10) { //Got stuck, didn't move if(mAdjAngle == 0) //Try going in various directions - mAdjAngle = 1.57079632679f; //pi/2 - else if (mAdjAngle == 1.57079632679f) - mAdjAngle = -1.57079632679f; + mAdjAngle = osg::PI / 2; + else if (mAdjAngle == osg::PI / 2) + mAdjAngle = -osg::PI / 2; else mAdjAngle = 0; mDuration = 1; //reset timer diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index d36c5930f..caacf8cb6 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -300,9 +300,8 @@ namespace MWMechanics bool isWaterCreature = actor.getClass().isPureWaterCreature(actor); do { // Determine a random location within radius of original position - const float pi = 3.14159265359f; const float wanderRadius = (0.2f + Misc::Rng::rollClosedProbability() * 0.8f) * wanderDistance; - const float randomDirection = Misc::Rng::rollClosedProbability() * 2.0f * pi; + const float randomDirection = Misc::Rng::rollClosedProbability() * 2.0f * osg::PI; const float destinationX = mInitialActorPosition.x() + wanderRadius * std::cos(randomDirection); const float destinationY = mInitialActorPosition.y() + wanderRadius * std::sin(randomDirection); const float destinationZ = mInitialActorPosition.z(); diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 90d25ee3e..31da729ea 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -486,7 +487,7 @@ namespace MWPhysics physicActor->setInertialForce(osg::Vec3f(0.f, 0.f, 0.f)); else { - inertia.z() += time * -627.2f; + inertia.z() -= time * Constants::GravityConst * Constants::UnitsPerMeter; if (inertia.z() < 0) inertia.z() *= slowFall; if (slowFall < 1.f) { diff --git a/apps/openmw/mwrender/globalmap.cpp b/apps/openmw/mwrender/globalmap.cpp index fae524faa..65820e577 100644 --- a/apps/openmw/mwrender/globalmap.cpp +++ b/apps/openmw/mwrender/globalmap.cpp @@ -273,9 +273,9 @@ namespace MWRender void GlobalMap::worldPosToImageSpace(float x, float z, float& imageX, float& imageY) { - imageX = float(x / 8192.f - mMinX) / (mMaxX - mMinX + 1); + imageX = float(x / float(Constants::CellSizeInUnits) - mMinX) / (mMaxX - mMinX + 1); - imageY = 1.f-float(z / 8192.f - mMinY) / (mMaxY - mMinY + 1); + imageY = 1.f-float(z / float(Constants::CellSizeInUnits) - mMinY) / (mMaxY - mMinY + 1); } void GlobalMap::cellTopLeftCornerToImageSpace(int x, int y, float& imageX, float& imageY) diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index 8ed3441de..8ea4e3991 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -71,7 +72,7 @@ namespace MWRender LocalMap::LocalMap(osg::Group* root) : mRoot(root) , mMapResolution(Settings::Manager::getInt("local map resolution", "Map")) - , mMapWorldSize(8192.f) + , mMapWorldSize(Constants::CellSizeInUnits) , mCellDistance(Settings::Manager::getInt("local map cell distance", "Map")) , mAngle(0.f) , mInterior(false) diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index 52832ad87..8fd47d2c5 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -27,6 +27,8 @@ #include +#include + #include #include @@ -401,7 +403,7 @@ Water::Water(osg::Group *parent, osg::Group* sceneRoot, Resource::ResourceSystem { mSimulation.reset(new RippleSimulation(parent, resourceSystem, fallback)); - mWaterGeom = SceneUtil::createWaterGeometry(CELL_SIZE*150, 40, 900); + mWaterGeom = SceneUtil::createWaterGeometry(Constants::CellSizeInUnits*150, 40, 900); mWaterGeom->setDrawCallback(new DepthClampCallback); mWaterGeom->setNodeMask(Mask_Water); @@ -679,7 +681,8 @@ bool Water::isUnderwater(const osg::Vec3f &pos) const osg::Vec3f Water::getSceneNodeCoordinates(int gridX, int gridY) { - return osg::Vec3f(static_cast(gridX * CELL_SIZE + (CELL_SIZE / 2)), static_cast(gridY * CELL_SIZE + (CELL_SIZE / 2)), mTop); + return osg::Vec3f(static_cast(gridX * Constants::CellSizeInUnits + (Constants::CellSizeInUnits / 2)), + static_cast(gridY * Constants::CellSizeInUnits + (Constants::CellSizeInUnits / 2)), mTop); } void Water::addEmitter (const MWWorld::Ptr& ptr, float scale, float force) diff --git a/apps/openmw/mwrender/water.hpp b/apps/openmw/mwrender/water.hpp index e2413cfa0..32a7977d2 100644 --- a/apps/openmw/mwrender/water.hpp +++ b/apps/openmw/mwrender/water.hpp @@ -50,8 +50,6 @@ namespace MWRender /// Water rendering class Water { - static const int CELL_SIZE = 8192; - osg::ref_ptr mRainIntensityUniform; osg::ref_ptr mParent; diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index 833b40e38..cadcdc4ab 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -34,10 +35,6 @@ namespace { -// The game uses 64 units per yard, or approximately 69.99125109 units per meter. -// Should this be defined publically somewhere? -const float UnitsPerMeter = 69.99125109f; - const int sLoudnessFPS = 20; // loudness values per second of audio ALCenum checkALCError(ALCdevice *device, const char *func, int line) @@ -818,13 +815,13 @@ bool OpenAL_Output::init(const std::string &devname, const std::string &hrtfname LoadEffect(mWaterEffect, EFX_REVERB_PRESET_UNDERWATER); } - alListenerf(AL_METERS_PER_UNIT, 1.0f / UnitsPerMeter); + alListenerf(AL_METERS_PER_UNIT, 1.0f / Constants::UnitsPerMeter); } skip_efx: alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED); - // Speed of sound is in units per second. Given the default speed of sound is 343.3 (assumed + // Speed of sound is in units per second. Take the sound speed in air (assumed // meters per second), multiply by the units per meter to get the speed in u/s. - alSpeedOfSound(343.3f * UnitsPerMeter); + alSpeedOfSound(Constants::SoundSpeedInAir * Constants::UnitsPerMeter); alGetError(); mInitialized = true; @@ -1400,8 +1397,7 @@ void OpenAL_Output::updateListener(const osg::Vec3f &pos, const osg::Vec3f &atdi if(env != mListenerEnv) { - // Speed of sound in water is 1484m/s, and in air is 343.3m/s (roughly) - alSpeedOfSound(((env == Env_Underwater) ? 1484.0f : 343.3f) * UnitsPerMeter); + alSpeedOfSound(((env == Env_Underwater) ? Constants::SoundSpeedUnderwater : Constants::SoundSpeedInAir) * Constants::UnitsPerMeter); // Update active sources with the environment's direct filter if(mWaterFilter) diff --git a/apps/openmw/mwworld/projectilemanager.cpp b/apps/openmw/mwworld/projectilemanager.cpp index 21218fab0..4698ba011 100644 --- a/apps/openmw/mwworld/projectilemanager.cpp +++ b/apps/openmw/mwworld/projectilemanager.cpp @@ -10,6 +10,8 @@ #include #include +#include + #include #include @@ -460,7 +462,7 @@ namespace MWWorld { // gravity constant - must be way lower than the gravity affecting actors, since we're not // simulating aerodynamics at all - it->mVelocity -= osg::Vec3f(0, 0, 627.2f * 0.1f) * duration; + it->mVelocity -= osg::Vec3f(0, 0, Constants::GravityConst * Constants::UnitsPerMeter * 0.1f) * duration; osg::Vec3f pos(it->mNode->getPosition()); osg::Vec3f newPos = pos + it->mVelocity * duration; diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index ed6dde310..dc3d4fd30 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -345,7 +345,7 @@ namespace MWWorld getGridCenter(cellX, cellY); float centerX, centerY; MWBase::Environment::get().getWorld()->indexToPosition(cellX, cellY, centerX, centerY, true); - const float maxDistance = 8192/2 + mCellLoadingThreshold; // 1/2 cell size + threshold + const float maxDistance = Constants::CellSizeInUnits / 2 + mCellLoadingThreshold; // 1/2 cell size + threshold float distance = std::max(std::abs(centerX-pos.x()), std::abs(centerY-pos.y())); if (distance > maxDistance) { @@ -793,7 +793,7 @@ namespace MWWorld float dist = std::max(std::abs(thisCellCenterX - playerPos.x()), std::abs(thisCellCenterY - playerPos.y())); dist = std::min(dist,std::max(std::abs(thisCellCenterX - predictedPos.x()), std::abs(thisCellCenterY - predictedPos.y()))); - float loadDist = 8192/2 + 8192 - mCellLoadingThreshold + mPreloadDistance; + float loadDist = Constants::CellSizeInUnits / 2 + Constants::CellSizeInUnits - mCellLoadingThreshold + mPreloadDistance; if (dist < loadDist) preloadCell(MWBase::Environment::get().getWorld()->getExterior(cellX+dx, cellY+dy)); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index d1f274787..2afcdffc8 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -260,7 +261,7 @@ namespace MWWorld if (!getPlayerPtr().isInCell()) { ESM::Position pos; - const int cellSize = 8192; + const int cellSize = Constants::CellSizeInUnits; pos.pos[0] = cellSize/2; pos.pos[1] = cellSize/2; pos.pos[2] = 0; @@ -1434,7 +1435,7 @@ namespace MWWorld void World::indexToPosition (int cellX, int cellY, float &x, float &y, bool centre) const { - const int cellSize = 8192; + const int cellSize = Constants::CellSizeInUnits; x = static_cast(cellSize * cellX); y = static_cast(cellSize * cellY); @@ -1448,10 +1449,8 @@ namespace MWWorld void World::positionToIndex (float x, float y, int &cellX, int &cellY) const { - const int cellSize = 8192; - - cellX = static_cast(std::floor(x / cellSize)); - cellY = static_cast(std::floor(y / cellSize)); + cellX = static_cast(std::floor(x / Constants::CellSizeInUnits)); + cellY = static_cast(std::floor(y / Constants::CellSizeInUnits)); } void World::queueMovement(const Ptr &ptr, const osg::Vec3f &velocity) @@ -3245,9 +3244,9 @@ namespace MWWorld float World::feetToGameUnits(float feet) { - // Looks like there is no GMST for this. This factor was determined in experiments - // with the Telekinesis effect. - return feet * 22; + // Original engine rounds size upward + static const int unitsPerFoot = ceil(Constants::UnitsPerFoot); + return feet * unitsPerFoot; } float World::getActivationDistancePlusTelekinesis() diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 8e798455f..2da7c80bf 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -85,7 +85,7 @@ add_component_dir (esmterrain ) add_component_dir (misc - utf8stream stringops resourcehelpers rng messageformatparser + constants utf8stream stringops resourcehelpers rng messageformatparser ) add_component_dir (debug diff --git a/components/esm/loadland.hpp b/components/esm/loadland.hpp index cccb472de..eaf766442 100644 --- a/components/esm/loadland.hpp +++ b/components/esm/loadland.hpp @@ -3,6 +3,8 @@ #include +#include + #include "esmcommon.hpp" namespace ESM @@ -53,7 +55,7 @@ struct Land static const int LAND_SIZE = 65; // cell terrain size in world coords - static const int REAL_SIZE = 8192; + static const int REAL_SIZE = Constants::CellSizeInUnits; // total number of vertices static const int LAND_NUM_VERTS = LAND_SIZE * LAND_SIZE; diff --git a/components/esmterrain/storage.cpp b/components/esmterrain/storage.cpp index f77e66276..0fa28cf0b 100644 --- a/components/esmterrain/storage.cpp +++ b/components/esmterrain/storage.cpp @@ -267,8 +267,8 @@ namespace ESMTerrain height = heightData->mHeights[col*ESM::Land::LAND_SIZE + row]; (*positions)[static_cast(vertX*numVerts + vertY)] - = osg::Vec3f((vertX / float(numVerts - 1) - 0.5f) * size * 8192, - (vertY / float(numVerts - 1) - 0.5f) * size * 8192, + = osg::Vec3f((vertX / float(numVerts - 1) - 0.5f) * size * Constants::CellSizeInUnits, + (vertY / float(numVerts - 1) - 0.5f) * size * Constants::CellSizeInUnits, height); if (normalData) @@ -477,8 +477,8 @@ namespace ESMTerrain float Storage::getHeightAt(const osg::Vec3f &worldPos) { - int cellX = static_cast(std::floor(worldPos.x() / 8192.f)); - int cellY = static_cast(std::floor(worldPos.y() / 8192.f)); + int cellX = static_cast(std::floor(worldPos.x() / float(Constants::CellSizeInUnits))); + int cellY = static_cast(std::floor(worldPos.y() / float(Constants::CellSizeInUnits))); osg::ref_ptr land = getLand(cellX, cellY); if (!land) @@ -491,8 +491,8 @@ namespace ESMTerrain // Mostly lifted from Ogre::Terrain::getHeightAtTerrainPosition // Normalized position in the cell - float nX = (worldPos.x() - (cellX * 8192))/8192.f; - float nY = (worldPos.y() - (cellY * 8192))/8192.f; + float nX = (worldPos.x() - (cellX * Constants::CellSizeInUnits)) / float(Constants::CellSizeInUnits); + float nY = (worldPos.y() - (cellY * Constants::CellSizeInUnits)) / float(Constants::CellSizeInUnits); // get left / bottom points (rounded down) float factor = ESM::Land::LAND_SIZE - 1.0f; @@ -524,10 +524,10 @@ namespace ESMTerrain */ // Build all 4 positions in normalized cell space, using point-sampled height - osg::Vec3f v0 (startXTS, startYTS, getVertexHeight(data, startX, startY) / 8192.f); - osg::Vec3f v1 (endXTS, startYTS, getVertexHeight(data, endX, startY) / 8192.f); - osg::Vec3f v2 (endXTS, endYTS, getVertexHeight(data, endX, endY) / 8192.f); - osg::Vec3f v3 (startXTS, endYTS, getVertexHeight(data, startX, endY) / 8192.f); + osg::Vec3f v0 (startXTS, startYTS, getVertexHeight(data, startX, startY) / float(Constants::CellSizeInUnits)); + osg::Vec3f v1 (endXTS, startYTS, getVertexHeight(data, endX, startY) / float(Constants::CellSizeInUnits)); + osg::Vec3f v2 (endXTS, endYTS, getVertexHeight(data, endX, endY) / float(Constants::CellSizeInUnits)); + osg::Vec3f v3 (startXTS, endYTS, getVertexHeight(data, startX, endY) / float(Constants::CellSizeInUnits)); // define this plane in terrain space osg::Plane plane; // FIXME: deal with differing triangle alignment @@ -555,7 +555,7 @@ namespace ESMTerrain // Solve plane equation for z return (-plane.getNormal().x() * nX -plane.getNormal().y() * nY - - plane[3]) / plane.getNormal().z() * 8192; + - plane[3]) / plane.getNormal().z() * Constants::CellSizeInUnits; } diff --git a/components/misc/constants.hpp b/components/misc/constants.hpp new file mode 100644 index 000000000..7174ae888 --- /dev/null +++ b/components/misc/constants.hpp @@ -0,0 +1,27 @@ +#ifndef OPENMW_CONSTANTS_H +#define OPENMW_CONSTANTS_H + +namespace Constants +{ + +// The game uses 64 units per yard +const float UnitsPerMeter = 69.99125109f; +const float UnitsPerFoot = 21.33333333f; + +// Sound speed in meters per second +const float SoundSpeedInAir = 343.3f; +const float SoundSpeedUnderwater = 1484.0f; + +// Gravity constant in m/sec^2 +// Note: 8.96 m/sec^2 = 9.8 yards/sec^2 +// Probaly original engine's developers just forgot +// that their engine uses yards instead of meters +// and used standart gravity value as it is +const float GravityConst = 8.96f; + +// Size of one exterior cell in game units +const int CellSizeInUnits = 8192; + +} + +#endif diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index e75deb2f7..9b9bbccd6 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -4,6 +4,8 @@ #include +#include + #include "quadtreenode.hpp" #include "storage.hpp" #include "viewdata.hpp" @@ -81,7 +83,7 @@ public: { float dist = distanceToBox(node->getBoundingBox(), eyePoint); int nativeLodLevel = Log2(static_cast(node->getSize()/mMinSize)); - int lodLevel = Log2(static_cast(dist/(8192*mMinSize))); + int lodLevel = Log2(static_cast(dist/(Constants::CellSizeInUnits*mMinSize))); return nativeLodLevel <= lodLevel; } From dda02bd69618fdf4e1d04a3e54c52c94718d7405 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Mon, 17 Sep 2018 19:56:36 +0400 Subject: [PATCH 049/163] Do not use book fonts for dialogue window --- apps/openmw/mwgui/bookpage.cpp | 25 ++++++++++--------------- apps/openmw/mwgui/bookpage.hpp | 2 +- apps/openmw/mwgui/dialogue.cpp | 10 +++++----- 3 files changed, 16 insertions(+), 21 deletions(-) diff --git a/apps/openmw/mwgui/bookpage.cpp b/apps/openmw/mwgui/bookpage.cpp index 8e264312e..c3b8935e8 100644 --- a/apps/openmw/mwgui/bookpage.cpp +++ b/apps/openmw/mwgui/bookpage.cpp @@ -264,29 +264,24 @@ struct TypesetBookImpl::Typesetter : BookTypesetter { } - Style * createStyle (const std::string& fontName, const Colour& fontColour) + Style * createStyle (const std::string& fontName, const Colour& fontColour, bool useBookFont) { - const std::string templateName = "Journalbook "; - std::string bookFont; + std::string fullFontName; if (fontName.empty()) - { - bookFont = MyGUI::FontManager::getInstance().getDefaultFont(); - bookFont = templateName + bookFont; - return createStyle(bookFont, fontColour); - } - - if (fontName.compare(0, templateName.size(), templateName) == 0) - bookFont = fontName; + fullFontName = MyGUI::FontManager::getInstance().getDefaultFont(); else - bookFont = templateName + bookFont; + fullFontName = fontName; + + if (useBookFont) + fullFontName = "Journalbook " + fullFontName; for (Styles::iterator i = mBook->mStyles.begin (); i != mBook->mStyles.end (); ++i) - if (i->match (bookFont.c_str(), fontColour, fontColour, fontColour, 0)) + if (i->match (fullFontName.c_str(), fontColour, fontColour, fontColour, 0)) return &*i; - MyGUI::IFont* font = MyGUI::FontManager::getInstance().getByName(bookFont); + MyGUI::IFont* font = MyGUI::FontManager::getInstance().getByName(fullFontName); if (!font) - throw std::runtime_error(std::string("can't find font ") + bookFont); + throw std::runtime_error(std::string("can't find font ") + fullFontName); StyleImpl & style = *mBook->mStyles.insert (mBook->mStyles.end (), StyleImpl ()); style.mFont = font; diff --git a/apps/openmw/mwgui/bookpage.hpp b/apps/openmw/mwgui/bookpage.hpp index c75f00ca4..4cf99794d 100644 --- a/apps/openmw/mwgui/bookpage.hpp +++ b/apps/openmw/mwgui/bookpage.hpp @@ -102,7 +102,7 @@ namespace MWGui static Ptr create (int pageWidth, int pageHeight); /// Create a simple text style consisting of a font and a text color. - virtual Style* createStyle (const std::string& fontName, const Colour& colour) = 0; + virtual Style* createStyle (const std::string& fontName, const Colour& colour, bool useBookFont=true) = 0; /// Create a hyper-link style with a user-defined identifier based on an /// existing style. The unique flag forces a new instance of this style diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index 6aceccaff..3ac8839e2 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -137,7 +137,7 @@ namespace MWGui if (mTitle != "") { const MyGUI::Colour& headerColour = MWBase::Environment::get().getWindowManager()->getTextColours().header; - BookTypesetter::Style* title = typesetter->createStyle("", headerColour); + BookTypesetter::Style* title = typesetter->createStyle("", headerColour, false); typesetter->write(title, to_utf8_span(mTitle.c_str())); typesetter->sectionBreak(); } @@ -182,7 +182,7 @@ namespace MWGui { const TextColours& textColours = MWBase::Environment::get().getWindowManager()->getTextColours(); - BookTypesetter::Style* style = typesetter->createStyle("", textColours.normal); + BookTypesetter::Style* style = typesetter->createStyle("", textColours.normal, false); size_t formatted = 0; // points to the first character that is not laid out yet for (std::map::iterator it = hyperLinks.begin(); it != hyperLinks.end(); ++it) { @@ -223,7 +223,7 @@ namespace MWGui { const TextColours& textColours = MWBase::Environment::get().getWindowManager()->getTextColours(); - BookTypesetter::Style* style = typesetter->createStyle("", textColours.normal); + BookTypesetter::Style* style = typesetter->createStyle("", textColours.normal, false); if (topicId) @@ -239,7 +239,7 @@ namespace MWGui void Message::write(BookTypesetter::Ptr typesetter, KeywordSearchT* keywordSearch, std::map& topicLinks) const { const MyGUI::Colour& textColour = MWBase::Environment::get().getWindowManager()->getTextColours().notify; - BookTypesetter::Style* title = typesetter->createStyle("", textColour); + BookTypesetter::Style* title = typesetter->createStyle("", textColour, false); typesetter->sectionBreak(9); typesetter->write(title, to_utf8_span(mText.c_str())); } @@ -567,7 +567,7 @@ namespace MWGui (*it)->write(typesetter, &mKeywordSearch, mTopicLinks); - BookTypesetter::Style* body = typesetter->createStyle("", MyGUI::Colour::White); + BookTypesetter::Style* body = typesetter->createStyle("", MyGUI::Colour::White, false); typesetter->sectionBreak(9); // choices From 6d0e6ab01220438a80bede2fb56dc5a4537da592 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Mon, 17 Sep 2018 22:24:21 +0400 Subject: [PATCH 050/163] Use book font by default in the text formatter --- apps/openmw/mwgui/formatting.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/formatting.hpp b/apps/openmw/mwgui/formatting.hpp index 6d98d48b3..b16a4e57d 100644 --- a/apps/openmw/mwgui/formatting.hpp +++ b/apps/openmw/mwgui/formatting.hpp @@ -14,7 +14,7 @@ namespace MWGui { TextStyle() : mColour(0,0,0) - , mFont("") + , mFont("Journalbook Magic Cards") , mTextSize(16) { } From 989de05f8046105c15cb6aa49280841a037ecde6 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 18 Sep 2018 15:23:16 +0400 Subject: [PATCH 051/163] Do not show duration for infinite light sources as -1 --- apps/openmw/mwclass/light.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index 27a9f3f2b..3ee228013 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -161,7 +161,12 @@ namespace MWClass std::string text; if (Settings::Manager::getBool("show effect duration","Game")) - text += "\n#{sDuration}: " + MWGui::ToolTips::toString(ptr.getClass().getRemainingUsageTime(ptr)); + { + // -1 is infinite light source, so duration makes no sense here. Other negative values are treated as 0. + float remainingTime = ptr.getClass().getRemainingUsageTime(ptr); + if (remainingTime != -1.0f) + text += "\n#{sDuration}: " + MWGui::ToolTips::toString(std::max(0.f, remainingTime)); + } text += MWGui::ToolTips::getWeightString(ref->mBase->mData.mWeight, "#{sWeight}"); text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}"); From 62f6f174cf2a6648c6d0c0da71205b5a97295a44 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 18 Sep 2018 15:36:37 +0400 Subject: [PATCH 052/163] Show attack speed of ranged weapons --- apps/openmw/mwclass/weapon.cpp | 13 +++++++++---- docs/source/reference/modding/settings/game.rst | 2 +- files/settings-default.cfg | 2 +- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index 466ae4716..6776434ec 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -65,7 +65,7 @@ namespace MWClass { const MWWorld::LiveCellRef *ref = ptr.get(); - return (ref->mBase->mData.mType < 11); // thrown weapons and arrows/bolts don't have health, only quantity + return (ref->mBase->mData.mType < ESM::Weapon::MarksmanThrown); // thrown weapons and arrows/bolts don't have health, only quantity } int Weapon::getItemMaxHealth (const MWWorld::ConstPtr& ptr) const @@ -318,21 +318,26 @@ namespace MWClass } } - if (ref->mBase->mData.mType < 11) // thrown weapons and arrows/bolts don't have health, only quantity + if (hasItemHealth(ptr)) { int remainingHealth = getItemHealth(ptr); text += "\n#{sCondition}: " + MWGui::ToolTips::toString(remainingHealth) + "/" + MWGui::ToolTips::toString(ref->mBase->mData.mHealth); } - // add reach and attack speed for melee weapon - if (ref->mBase->mData.mType < 9 && Settings::Manager::getBool("show melee info", "Game")) + const bool verbose = Settings::Manager::getBool("show melee info", "Game"); + // add reach for melee weapon + if (ref->mBase->mData.mType < ESM::Weapon::MarksmanBow && verbose) { // 64 game units = 1 yard = 3 ft, display value in feet const float combatDistance = store.get().find("fCombatDistance")->mValue.getFloat() * ref->mBase->mData.mReach; text += MWGui::ToolTips::getWeightString(combatDistance*3/64, "#{sRange}"); text += " #{sFeet}"; + } + // add attack speed for any weapon excepts arrows and bolts + if (ref->mBase->mData.mType < ESM::Weapon::Arrow && verbose) + { text += MWGui::ToolTips::getPercentString(ref->mBase->mData.mSpeed, "#{sAttributeSpeed}"); } diff --git a/docs/source/reference/modding/settings/game.rst b/docs/source/reference/modding/settings/game.rst index b733cc7c4..baeac92b2 100644 --- a/docs/source/reference/modding/settings/game.rst +++ b/docs/source/reference/modding/settings/game.rst @@ -36,7 +36,7 @@ show melee info :Range: True/False :Default: False -If this setting is true, the reach and speed of melee weapons will show on their tooltip. +If this setting is true, the reach and speed of weapons will show on their tooltip. This setting can only be configured by editing the settings configuration file. diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 45cee0559..3344e6bb4 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -188,7 +188,7 @@ show owned = 0 # Show damage bonus of arrow and bolts. show projectile damage = false -# Show additional melee weapon info: reach and attack speed +# Show additional weapon info: reach and attack speed show melee info = false # Show success probability in self-enchant dialog From 6f4f6a155fc5293a16fb55d945a3f01ce197db40 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 19 Sep 2018 12:04:58 +0400 Subject: [PATCH 053/163] Combat AI: take ranged weapon speed in account --- apps/openmw/mwmechanics/weaponpriority.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/weaponpriority.cpp b/apps/openmw/mwmechanics/weaponpriority.cpp index 818065ae4..aaa06f6e4 100644 --- a/apps/openmw/mwmechanics/weaponpriority.cpp +++ b/apps/openmw/mwmechanics/weaponpriority.cpp @@ -119,7 +119,7 @@ namespace MWMechanics rating *= getHitChance(actor, enemy, value) / 100.f; - if (weapon->mData.mType < ESM::Weapon::MarksmanBow) + if (weapon->mData.mType < ESM::Weapon::Arrow) rating *= weapon->mData.mSpeed; return rating * ratingMult; From 276b7830a9f03cea95f3a3382648b9e34046ed1f Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 18 Sep 2018 14:57:21 +0400 Subject: [PATCH 054/163] Mass potion creation (feature #4642) --- CHANGELOG.md | 1 + apps/openmw/mwgui/alchemywindow.cpp | 90 +++++++++++++++++++++++- apps/openmw/mwgui/alchemywindow.hpp | 24 +++++++ apps/openmw/mwmechanics/alchemy.cpp | 64 +++++++++++++++-- apps/openmw/mwmechanics/alchemy.hpp | 16 ++++- components/widgets/numericeditbox.cpp | 5 ++ components/widgets/numericeditbox.hpp | 1 + files/mygui/openmw_alchemy_window.layout | 15 +++- 8 files changed, 206 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1fb9837bd..958ec0118 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -155,6 +155,7 @@ Feature #4626: Weapon priority: account for weapon speed Feature #4632: AI priority: utilize vanilla AI GMSTs for priority rating Feature #4636: Use sTo GMST in spellmaking menu + Feature #4642: Batching potion creation Task #2490: Don't open command prompt window on Release-mode builds automatically Task #4545: Enable is_pod string test Task #4605: Optimize skinning diff --git a/apps/openmw/mwgui/alchemywindow.cpp b/apps/openmw/mwgui/alchemywindow.cpp index 6fd3d3220..0aa98dc4a 100644 --- a/apps/openmw/mwgui/alchemywindow.cpp +++ b/apps/openmw/mwgui/alchemywindow.cpp @@ -1,5 +1,7 @@ #include "alchemywindow.hpp" +#include + #include #include #include @@ -25,6 +27,9 @@ namespace MWGui { + const float AlchemyWindow::sCountChangeInitialPause = 0.5f; + const float AlchemyWindow::sCountChangeInterval = 0.1f; + AlchemyWindow::AlchemyWindow() : WindowBase("openmw_alchemy_window.layout") , mSortModel(NULL) @@ -43,9 +48,21 @@ namespace MWGui getWidget(mApparatus[2], "Apparatus3"); getWidget(mApparatus[3], "Apparatus4"); getWidget(mEffectsBox, "CreatedEffects"); + getWidget(mBrewCountEdit, "BrewCount"); + getWidget(mIncreaseButton, "IncreaseButton"); + getWidget(mDecreaseButton, "DecreaseButton"); getWidget(mNameEdit, "NameEdit"); getWidget(mItemView, "ItemView"); + mBrewCountEdit->eventValueChanged += MyGUI::newDelegate(this, &AlchemyWindow::onCountValueChanged); + mBrewCountEdit->eventEditSelectAccept += MyGUI::newDelegate(this, &AlchemyWindow::onAccept); + mBrewCountEdit->setMinValue(1); + mBrewCountEdit->setValue(1); + + mIncreaseButton->eventMouseButtonPressed += MyGUI::newDelegate(this, &AlchemyWindow::onIncreaseButtonPressed); + mIncreaseButton->eventMouseButtonReleased += MyGUI::newDelegate(this, &AlchemyWindow::onCountButtonReleased); + mDecreaseButton->eventMouseButtonPressed += MyGUI::newDelegate(this, &AlchemyWindow::onDecreaseButtonPressed); + mDecreaseButton->eventMouseButtonReleased += MyGUI::newDelegate(this, &AlchemyWindow::onCountButtonReleased); mItemView->eventItemClicked += MyGUI::newDelegate(this, &AlchemyWindow::onSelectedItem); @@ -77,7 +94,15 @@ namespace MWGui void AlchemyWindow::onCreateButtonClicked(MyGUI::Widget* _sender) { - MWMechanics::Alchemy::Result result = mAlchemy->create (mNameEdit->getCaption ()); + mAlchemy->setPotionName(mNameEdit->getCaption()); + int count = mAlchemy->countPotionsToBrew(); + count = std::min(count, mBrewCountEdit->getValue()); + createPotions(count); + } + + void AlchemyWindow::createPotions(int count) + { + MWMechanics::Alchemy::Result result = mAlchemy->create(mNameEdit->getCaption(), count); MWBase::WindowManager *winMgr = MWBase::Environment::get().getWindowManager(); switch (result) @@ -92,8 +117,11 @@ namespace MWGui winMgr->messageBox("#{sNotifyMessage6a}"); break; case MWMechanics::Alchemy::Result_Success: - winMgr->messageBox("#{sPotionSuccess}"); winMgr->playSound("potion success"); + if (count == 1) + winMgr->messageBox("#{sPotionSuccess}"); + else + winMgr->messageBox("#{sPotionSuccess} "+mNameEdit->getCaption()+" ("+std::to_string(count)+")"); break; case MWMechanics::Alchemy::Result_NoEffects: case MWMechanics::Alchemy::Result_RandomFailure: @@ -126,6 +154,7 @@ namespace MWGui mItemView->resetScrollBars(); mNameEdit->setCaption(""); + mBrewCountEdit->setValue(1); int index = 0; for (MWMechanics::Alchemy::TToolsIterator iter (mAlchemy->beginTools()); @@ -250,4 +279,61 @@ namespace MWGui update(); } + + void AlchemyWindow::addRepeatController(MyGUI::Widget *widget) + { + MyGUI::ControllerItem* item = MyGUI::ControllerManager::getInstance().createItem(Controllers::ControllerRepeatEvent::getClassTypeName()); + Controllers::ControllerRepeatEvent* controller = item->castType(); + controller->eventRepeatClick += MyGUI::newDelegate(this, &AlchemyWindow::onRepeatClick); + controller->setRepeat(sCountChangeInitialPause, sCountChangeInterval); + MyGUI::ControllerManager::getInstance().addItem(widget, controller); + } + + void AlchemyWindow::onIncreaseButtonPressed(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id) + { + addRepeatController(_sender); + onIncreaseButtonTriggered(); + } + + void AlchemyWindow::onDecreaseButtonPressed(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id) + { + addRepeatController(_sender); + onDecreaseButtonTriggered(); + } + + void AlchemyWindow::onRepeatClick(MyGUI::Widget* widget, MyGUI::ControllerItem* controller) + { + if (widget == mIncreaseButton) + onIncreaseButtonTriggered(); + else if (widget == mDecreaseButton) + onDecreaseButtonTriggered(); + } + + void AlchemyWindow::onCountButtonReleased(MyGUI::Widget *_sender, int _left, int _top, MyGUI::MouseButton _id) + { + MyGUI::ControllerManager::getInstance().removeItem(_sender); + } + + void AlchemyWindow::onCountValueChanged(int value) + { + mBrewCountEdit->setValue(std::abs(value)); + } + + void AlchemyWindow::onIncreaseButtonTriggered() + { + int currentCount = mBrewCountEdit->getValue(); + + // prevent overflows, and prevent entering INT_MIN since abs(INT_MIN) is undefined + if (currentCount == INT_MAX || currentCount == INT_MIN+1) + return; + + mBrewCountEdit->setValue(currentCount+1); + } + + void AlchemyWindow::onDecreaseButtonTriggered() + { + int currentCount = mBrewCountEdit->getValue(); + if (currentCount > 1) + mBrewCountEdit->setValue(currentCount-1); + } } diff --git a/apps/openmw/mwgui/alchemywindow.hpp b/apps/openmw/mwgui/alchemywindow.hpp index d1e54241a..aa23fbaa5 100644 --- a/apps/openmw/mwgui/alchemywindow.hpp +++ b/apps/openmw/mwgui/alchemywindow.hpp @@ -3,8 +3,13 @@ #include +#include + #include "../mwmechanics/alchemy.hpp" +#include + +#include "controllers.hpp" #include "windowbase.hpp" namespace MWMechanics @@ -28,6 +33,10 @@ namespace MWGui void onResChange(int, int) { center(); } private: + + static const float sCountChangeInitialPause; // in seconds + static const float sCountChangeInterval; // in seconds + std::string mSuggestedPotionName; ItemView* mItemView; @@ -38,17 +47,32 @@ namespace MWGui MyGUI::Widget* mEffectsBox; + MyGUI::Button* mIncreaseButton; + MyGUI::Button* mDecreaseButton; MyGUI::EditBox* mNameEdit; + Gui::NumericEditBox* mBrewCountEdit; void onCancelButtonClicked(MyGUI::Widget* _sender); void onCreateButtonClicked(MyGUI::Widget* _sender); void onIngredientSelected(MyGUI::Widget* _sender); void onAccept(MyGUI::EditBox*); + void onIncreaseButtonPressed(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id); + void onDecreaseButtonPressed(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id); + void onCountButtonReleased(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id); + void onCountValueChanged(int value); + void onRepeatClick(MyGUI::Widget* widget, MyGUI::ControllerItem* controller); + + void addRepeatController(MyGUI::Widget* widget); + + void onIncreaseButtonTriggered(); + void onDecreaseButtonTriggered(); void onSelectedItem(int index); void removeIngredient(MyGUI::Widget* ingredient); + void createPotions(int count); + void update(); std::unique_ptr mAlchemy; diff --git a/apps/openmw/mwmechanics/alchemy.cpp b/apps/openmw/mwmechanics/alchemy.cpp index b8f820307..98860198b 100644 --- a/apps/openmw/mwmechanics/alchemy.cpp +++ b/apps/openmw/mwmechanics/alchemy.cpp @@ -30,6 +30,7 @@ MWMechanics::Alchemy::Alchemy() : mValue(0) + , mPotionName("") { } @@ -336,6 +337,25 @@ int MWMechanics::Alchemy::countIngredients() const return ingredients; } +int MWMechanics::Alchemy::countPotionsToBrew() const +{ + Result readyStatus = getReadyStatus(); + if (readyStatus != Result_Success) + return 0; + + int toBrew = -1; + + for (TIngredientsIterator iter (beginIngredients()); iter!=endIngredients(); ++iter) + if (!iter->isEmpty()) + { + int count = iter->getRefData().getCount(); + if ((count > 0 && count < toBrew) || toBrew < 0) + toBrew = count; + } + + return toBrew; +} + void MWMechanics::Alchemy::setAlchemist (const MWWorld::Ptr& npc) { mAlchemist = npc; @@ -396,6 +416,12 @@ void MWMechanics::Alchemy::clear() mTools.clear(); mIngredients.clear(); mEffects.clear(); + setPotionName(""); +} + +void MWMechanics::Alchemy::setPotionName(const std::string& name) +{ + mPotionName = name; } int MWMechanics::Alchemy::addIngredient (const MWWorld::Ptr& ingredient) @@ -456,7 +482,7 @@ bool MWMechanics::Alchemy::knownEffect(unsigned int potionEffectIndex, const MWW || (potionEffectIndex <= 7 && alchemySkill >= fWortChanceValue*4); } -MWMechanics::Alchemy::Result MWMechanics::Alchemy::create (const std::string& name) +MWMechanics::Alchemy::Result MWMechanics::Alchemy::getReadyStatus() const { if (mTools[ESM::Apparatus::MortarPestle].isEmpty()) return Result_NoMortarAndPestle; @@ -464,15 +490,43 @@ MWMechanics::Alchemy::Result MWMechanics::Alchemy::create (const std::string& na if (countIngredients()<2) return Result_LessThanTwoIngredients; - if (name.empty()) + if (mPotionName.empty()) return Result_NoName; if (listEffects().empty()) - { - removeIngredients(); return Result_NoEffects; + + return Result_Success; +} + +MWMechanics::Alchemy::Result MWMechanics::Alchemy::create (const std::string& name, int& count) +{ + setPotionName(name); + Result readyStatus = getReadyStatus(); + + if (readyStatus == Result_NoEffects) + removeIngredients(); + + if (readyStatus != Result_Success) + return readyStatus; + + Result result = Result_RandomFailure; + int brewedCount = 0; + for (int i = 0; i < count; ++i) + { + if (createSingle() == Result_Success) + { + result = Result_Success; + brewedCount++; + } } + count = brewedCount; + return result; +} + +MWMechanics::Alchemy::Result MWMechanics::Alchemy::createSingle () +{ if (beginEffects() == endEffects()) { // all effects were nullified due to insufficient skill @@ -486,7 +540,7 @@ MWMechanics::Alchemy::Result MWMechanics::Alchemy::create (const std::string& na return Result_RandomFailure; } - addPotion (name); + addPotion(mPotionName); removeIngredients(); diff --git a/apps/openmw/mwmechanics/alchemy.hpp b/apps/openmw/mwmechanics/alchemy.hpp index f351881e0..9f9f0b21c 100644 --- a/apps/openmw/mwmechanics/alchemy.hpp +++ b/apps/openmw/mwmechanics/alchemy.hpp @@ -51,11 +51,14 @@ namespace MWMechanics TIngredientsContainer mIngredients; TEffectsContainer mEffects; int mValue; + std::string mPotionName; void applyTools (int flags, float& value) const; void updateEffects(); + Result getReadyStatus() const; + const ESM::Potion *getRecord(const ESM::Potion& toFind) const; ///< Try to find a potion record similar to \a toFind in the record store, or return 0 if not found /// \note Does not account for record ID, model or icon @@ -70,6 +73,10 @@ namespace MWMechanics void increaseSkill(); ///< Increase alchemist's skill. + Result createSingle (); + ///< Try to create a potion from the ingredients, place it in the inventory of the alchemist and + /// adjust the skills of the alchemist accordingly. + float getAlchemyFactor() const; int countIngredients() const; @@ -79,6 +86,8 @@ namespace MWMechanics TEffectsIterator endEffects() const; public: + int countPotionsToBrew() const; + ///< calculates maximum amount of potions, which you can make from selected ingredients static bool knownEffect (unsigned int potionEffectIndex, const MWWorld::Ptr& npc); ///< Does npc have sufficient alchemy skill to know about this potion effect? @@ -100,6 +109,9 @@ namespace MWMechanics void clear(); ///< Remove alchemist, tools and ingredients. + void setPotionName(const std::string& name); + ///< Set name of potion to create + std::set listEffects() const; ///< List all effects shared by at least two ingredients. @@ -115,8 +127,8 @@ namespace MWMechanics std::string suggestPotionName (); ///< Suggest a name for the potion, based on the current effects - Result create (const std::string& name); - ///< Try to create a potion from the ingredients, place it in the inventory of the alchemist and + Result create (const std::string& name, int& count); + ///< Try to create potions from the ingredients, place them in the inventory of the alchemist and /// adjust the skills of the alchemist accordingly. /// \param name must not be an empty string, or Result_NoName is returned }; diff --git a/components/widgets/numericeditbox.cpp b/components/widgets/numericeditbox.cpp index efe480653..e8ba226f7 100644 --- a/components/widgets/numericeditbox.cpp +++ b/components/widgets/numericeditbox.cpp @@ -59,6 +59,11 @@ namespace Gui } } + int NumericEditBox::getValue() + { + return mValue; + } + void NumericEditBox::setMinValue(int minValue) { mMinValue = minValue; diff --git a/components/widgets/numericeditbox.hpp b/components/widgets/numericeditbox.hpp index 3edae2fc7..137583d37 100644 --- a/components/widgets/numericeditbox.hpp +++ b/components/widgets/numericeditbox.hpp @@ -30,6 +30,7 @@ namespace Gui /// @note Does not trigger eventValueChanged void setValue (int value); + int getValue(); void setMinValue(int minValue); void setMaxValue(int maxValue); diff --git a/files/mygui/openmw_alchemy_window.layout b/files/mygui/openmw_alchemy_window.layout index edc9a4c3d..714872fc3 100644 --- a/files/mygui/openmw_alchemy_window.layout +++ b/files/mygui/openmw_alchemy_window.layout @@ -74,7 +74,20 @@ - + + + + + + + + + + + + + + From bdd9eba2b822a913cf12bbaf53f543e6f5a71701 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Thu, 20 Sep 2018 16:02:26 +0400 Subject: [PATCH 055/163] Use C++ limits instead of C ones --- apps/opencs/view/world/util.cpp | 4 +--- apps/openmw/mwgui/alchemywindow.cpp | 6 ++---- apps/openmw/mwgui/tradewindow.cpp | 6 ++---- apps/openmw/mwmechanics/autocalcspell.cpp | 9 ++++----- apps/openmw/mwrender/globalmap.cpp | 2 -- 5 files changed, 9 insertions(+), 18 deletions(-) diff --git a/apps/opencs/view/world/util.cpp b/apps/opencs/view/world/util.cpp index eab37e1bf..b194e460d 100644 --- a/apps/opencs/view/world/util.cpp +++ b/apps/opencs/view/world/util.cpp @@ -1,8 +1,6 @@ #include "util.hpp" #include -#include -#include #include #include @@ -209,7 +207,7 @@ QWidget *CSVWorld::CommandDelegate::createEditor (QWidget *parent, const QStyleO case CSMWorld::ColumnBase::Display_Integer: { DialogueSpinBox *sb = new DialogueSpinBox(parent); - sb->setRange(INT_MIN, INT_MAX); + sb->setRange(std::numeric_limits::min(), std::numeric_limits::max()); return sb; } diff --git a/apps/openmw/mwgui/alchemywindow.cpp b/apps/openmw/mwgui/alchemywindow.cpp index 0aa98dc4a..a599e6c7b 100644 --- a/apps/openmw/mwgui/alchemywindow.cpp +++ b/apps/openmw/mwgui/alchemywindow.cpp @@ -1,7 +1,5 @@ #include "alchemywindow.hpp" -#include - #include #include #include @@ -323,8 +321,8 @@ namespace MWGui { int currentCount = mBrewCountEdit->getValue(); - // prevent overflows, and prevent entering INT_MIN since abs(INT_MIN) is undefined - if (currentCount == INT_MAX || currentCount == INT_MIN+1) + // prevent overflows + if (currentCount == std::numeric_limits::max()) return; mBrewCountEdit->setValue(currentCount+1); diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index f461d5f50..726c72b99 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -1,7 +1,5 @@ #include "tradewindow.hpp" -#include - #include #include #include @@ -96,7 +94,7 @@ namespace MWGui mTotalBalance->eventValueChanged += MyGUI::newDelegate(this, &TradeWindow::onBalanceValueChanged); mTotalBalance->eventEditSelectAccept += MyGUI::newDelegate(this, &TradeWindow::onAccept); - mTotalBalance->setMinValue(INT_MIN+1); // disallow INT_MIN since abs(INT_MIN) is undefined + mTotalBalance->setMinValue(std::numeric_limits::min()+1); // disallow INT_MIN since abs(INT_MIN) is undefined setCoord(400, 0, 400, 300); } @@ -431,7 +429,7 @@ namespace MWGui void TradeWindow::onIncreaseButtonTriggered() { // prevent overflows, and prevent entering INT_MIN since abs(INT_MIN) is undefined - if (mCurrentBalance == INT_MAX || mCurrentBalance == INT_MIN+1) + if (mCurrentBalance == std::numeric_limits::max() || mCurrentBalance == std::numeric_limits::min()+1) return; if (mCurrentBalance < 0) mCurrentBalance -= 1; else mCurrentBalance += 1; diff --git a/apps/openmw/mwmechanics/autocalcspell.cpp b/apps/openmw/mwmechanics/autocalcspell.cpp index a52fcc059..80ac14ddd 100644 --- a/apps/openmw/mwmechanics/autocalcspell.cpp +++ b/apps/openmw/mwmechanics/autocalcspell.cpp @@ -1,7 +1,6 @@ #include "autocalcspell.hpp" #include "spellcasting.hpp" -#include #include #include "../mwworld/esmstore.hpp" @@ -50,7 +49,7 @@ namespace MWMechanics caps.mCount = 0; caps.mLimit = iAutoSpellSchoolMax[i]; caps.mReachedLimit = iAutoSpellSchoolMax[i] <= 0; - caps.mMinCost = INT_MAX; + caps.mMinCost = std::numeric_limits::max(); caps.mWeakestSpell.clear(); schoolCaps[i] = caps; } @@ -101,7 +100,7 @@ namespace MWMechanics if (found != selectedSpells.end()) selectedSpells.erase(found); - cap.mMinCost = INT_MAX; + cap.mMinCost = std::numeric_limits::max(); for (std::vector::iterator weakIt = selectedSpells.begin(); weakIt != selectedSpells.end(); ++weakIt) { const ESM::Spell* testSpell = spells.find(*weakIt); @@ -151,7 +150,7 @@ namespace MWMechanics float baseMagicka = fPCbaseMagickaMult * actorAttributes[ESM::Attribute::Intelligence]; bool reachedLimit = false; const ESM::Spell* weakestSpell = NULL; - int minCost = INT_MAX; + int minCost = std::numeric_limits::max(); std::vector selectedSpells; @@ -188,7 +187,7 @@ namespace MWMechanics if (it != selectedSpells.end()) selectedSpells.erase(it); - minCost = INT_MAX; + minCost = std::numeric_limits::max(); for (std::vector::iterator weakIt = selectedSpells.begin(); weakIt != selectedSpells.end(); ++weakIt) { const ESM::Spell* testSpell = esmStore.get().find(*weakIt); diff --git a/apps/openmw/mwrender/globalmap.cpp b/apps/openmw/mwrender/globalmap.cpp index fae524faa..520bd3cb7 100644 --- a/apps/openmw/mwrender/globalmap.cpp +++ b/apps/openmw/mwrender/globalmap.cpp @@ -1,7 +1,5 @@ #include "globalmap.hpp" -#include - #include #include #include From 01b3f92f7eac5d792bf910b966b441605c01ea07 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Thu, 13 Sep 2018 13:21:38 +0400 Subject: [PATCH 056/163] Make coc and fixme console commands close to vanilla (bug #4292, bug #4217) --- apps/openmw/mwscript/cellextensions.cpp | 12 +-- .../mwscript/transformationextensions.cpp | 6 +- apps/openmw/mwworld/worldimp.cpp | 77 +++++++++++++++---- components/compiler/extensions0.cpp | 2 +- components/compiler/opcodes.hpp | 1 - 5 files changed, 71 insertions(+), 27 deletions(-) diff --git a/apps/openmw/mwscript/cellextensions.cpp b/apps/openmw/mwscript/cellextensions.cpp index 2cb76ef88..f01fed2ce 100644 --- a/apps/openmw/mwscript/cellextensions.cpp +++ b/apps/openmw/mwscript/cellextensions.cpp @@ -46,18 +46,19 @@ namespace MWScript ESM::Position pos; MWBase::World *world = MWBase::Environment::get().getWorld(); + const MWWorld::Ptr playerPtr = world->getPlayerPtr(); if (world->findExteriorPosition(cell, pos)) { - MWWorld::ActionTeleport("", pos, false).execute(world->getPlayerPtr()); - world->fixPosition(world->getPlayerPtr()); + MWWorld::ActionTeleport("", pos, false).execute(playerPtr); + world->adjustPosition(playerPtr, false); } else { // Change to interior even if findInteriorPosition() // yields false. In this case position will be zero-point. world->findInteriorPosition(cell, pos); - MWWorld::ActionTeleport(cell, pos, false).execute(world->getPlayerPtr()); + MWWorld::ActionTeleport(cell, pos, false).execute(playerPtr); } } }; @@ -76,14 +77,15 @@ namespace MWScript ESM::Position pos; MWBase::World *world = MWBase::Environment::get().getWorld(); + const MWWorld::Ptr playerPtr = world->getPlayerPtr(); world->indexToPosition (x, y, pos.pos[0], pos.pos[1], true); pos.pos[2] = 0; pos.rot[0] = pos.rot[1] = pos.rot[2] = 0; - MWWorld::ActionTeleport("", pos, false).execute(world->getPlayerPtr()); - world->fixPosition(world->getPlayerPtr()); + MWWorld::ActionTeleport("", pos, false).execute(playerPtr); + world->adjustPosition(playerPtr, false); } }; diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index 16d7e8036..9d7a5c6ed 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -732,14 +732,13 @@ namespace MWScript } }; - template class OpFixme : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { - MWWorld::Ptr ptr = R()(runtime); + const MWWorld::Ptr ptr = MWMechanics::getPlayer(); MWBase::Environment::get().getWorld()->fixPosition(ptr); } }; @@ -784,8 +783,7 @@ namespace MWScript interpreter.installSegment5(Compiler::Transformation::opcodeGetStartingAngle, new OpGetStartingAngle); interpreter.installSegment5(Compiler::Transformation::opcodeGetStartingAngleExplicit, new OpGetStartingAngle); interpreter.installSegment5(Compiler::Transformation::opcodeResetActors, new OpResetActors); - interpreter.installSegment5(Compiler::Transformation::opcodeFixme, new OpFixme); - interpreter.installSegment5(Compiler::Transformation::opcodeFixmeExplicit, new OpFixme); + interpreter.installSegment5(Compiler::Transformation::opcodeFixme, new OpFixme); } } } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 2afcdffc8..29161c298 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -246,7 +246,7 @@ namespace MWWorld if (findExteriorPosition (mStartCell, pos)) { changeToExteriorCell (pos, true); - fixPosition(getPlayerPtr()); + adjustPosition(getPlayerPtr(), false); } else { @@ -1353,13 +1353,37 @@ namespace MWWorld void World::fixPosition(const Ptr &actor) { - const float dist = 8000; - osg::Vec3f pos (actor.getRefData().getPosition().asVec3()); - pos.z() += dist; + const float distance = 128.f; + ESM::Position esmPos = actor.getRefData().getPosition(); + osg::Quat orientation(esmPos.rot[2], osg::Vec3f(0,0,-1)); + osg::Vec3f pos (esmPos.asVec3()); - osg::Vec3f traced = mPhysics->traceDown(actor, pos, dist*1.1f); + int direction = 0; + int fallbackDirections[4] = {direction, (direction+3)%4, (direction+2)%4, (direction+1)%4}; + + osg::Vec3f targetPos = pos; + for (int i=0; i<4; ++i) + { + direction = fallbackDirections[i]; + if (direction == 0) targetPos = pos + (orientation * osg::Vec3f(0,1,0)) * distance; + else if(direction == 1) targetPos = pos - (orientation * osg::Vec3f(0,1,0)) * distance; + else if(direction == 2) targetPos = pos - (orientation * osg::Vec3f(1,0,0)) * distance; + else if(direction == 3) targetPos = pos + (orientation * osg::Vec3f(1,0,0)) * distance; + + // destination is free + if (!castRay(pos.x(), pos.y(), pos.z(), targetPos.x(), targetPos.y(), targetPos.z())) + break; + } + + targetPos.z() += distance / 2.f; // move up a bit to get out from geometry, will snap down later + osg::Vec3f traced = mPhysics->traceDown(actor, targetPos, Constants::CellSizeInUnits); if (traced != pos) - moveObject(actor, actor.getCell(), traced.x(), traced.y(), traced.z()); + { + esmPos.pos[0] = traced.x(); + esmPos.pos[1] = traced.y(); + esmPos.pos[2] = traced.z(); + MWWorld::ActionTeleport("", esmPos, false).execute(actor); + } } void World::rotateObject (const Ptr& ptr,float x,float y,float z, bool adjust) @@ -1429,7 +1453,7 @@ namespace MWWorld ipos.rot[2] = referenceObject.getRefData().getPosition().rot[2]; MWWorld::Ptr placed = copyObjectToCell(ptr, referenceCell, ipos, ptr.getRefData().getCount(), false); - placed.getClass().adjustPosition(placed, true); // snap to ground + adjustPosition(placed, true); // snap to ground return placed; } @@ -2604,31 +2628,52 @@ namespace MWWorld return false; } + std::vector sortedDoors; const DoorList &doors = cellStore->getReadOnlyDoors().mList; - for (DoorList::const_iterator it = doors.begin(); it != doors.end(); ++it) { - if (!it->mRef.getTeleport()) { + for (DoorList::const_iterator it = doors.begin(); it != doors.end(); ++it) + { + if (!it->mRef.getTeleport()) + { continue; } + sortedDoors.push_back(&it->mRef); + } + + // Sort teleporting doors alphabetically, first by ID, then by destination cell to make search consistent + std::sort(sortedDoors.begin(), sortedDoors.end(), [] (const MWWorld::CellRef *lhs, const MWWorld::CellRef *rhs) + { + if (lhs->getRefId() != rhs->getRefId()) + return lhs->getRefId() < rhs->getRefId(); + + return lhs->getDestCell() < rhs->getDestCell(); + }); + + for (std::vector::const_iterator it = sortedDoors.begin(); it != sortedDoors.end(); ++it) + { MWWorld::CellStore *source = 0; // door to exterior - if (it->mRef.getDestCell().empty()) { + if ((*it)->getDestCell().empty()) + { int x, y; - ESM::Position doorDest = it->mRef.getDoorDest(); + ESM::Position doorDest = (*it)->getDoorDest(); positionToIndex(doorDest.pos[0], doorDest.pos[1], x, y); source = getExterior(x, y); } // door to interior - else { - source = getInterior(it->mRef.getDestCell()); + else + { + source = getInterior((*it)->getDestCell()); } - if (0 != source) { + if (0 != source) + { // Find door leading to our current teleport door // and use its destination to position inside cell. const DoorList &destinationDoors = source->getReadOnlyDoors().mList; - for (DoorList::const_iterator jt = destinationDoors.begin(); jt != destinationDoors.end(); ++jt) { - if (it->mRef.getTeleport() && + for (DoorList::const_iterator jt = destinationDoors.begin(); jt != destinationDoors.end(); ++jt) + { + if ((*it)->getTeleport() && Misc::StringUtils::ciEqual(name, jt->mRef.getDestCell())) { /// \note Using _any_ door pointed to the interior, diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index 7638d0f78..7979f06b9 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -546,7 +546,7 @@ namespace Compiler extensions.registerInstruction("moveworld","cf",opcodeMoveWorld,opcodeMoveWorldExplicit); extensions.registerFunction("getstartingangle",'f',"c",opcodeGetStartingAngle,opcodeGetStartingAngleExplicit); extensions.registerInstruction("resetactors","",opcodeResetActors); - extensions.registerInstruction("fixme","",opcodeFixme, opcodeFixmeExplicit); + extensions.registerInstruction("fixme","",opcodeFixme); extensions.registerInstruction("ra","",opcodeResetActors); } } diff --git a/components/compiler/opcodes.hpp b/components/compiler/opcodes.hpp index aef92b311..c141eec68 100644 --- a/components/compiler/opcodes.hpp +++ b/components/compiler/opcodes.hpp @@ -504,7 +504,6 @@ namespace Compiler const int opcodeMoveWorldExplicit = 0x2000209; const int opcodeResetActors = 0x20002f4; const int opcodeFixme = 0x2000302; - const int opcodeFixmeExplicit = 0x2000303; } namespace User From fb484c6fde5b05bfaf2927bccd80caf037a53230 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Fri, 21 Sep 2018 19:02:28 +0400 Subject: [PATCH 057/163] Optimize AI loop a bit --- apps/openmw/mwmechanics/actors.cpp | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 0ee49ac45..e3b1ec791 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -1303,6 +1303,16 @@ namespace MWMechanics std::map > cachedAllies; // will be filled as engageCombat iterates + bool aiActive = MWBase::Environment::get().getMechanicsManager()->isAIActive(); + int attackedByPlayerId = player.getClass().getCreatureStats(player).getHitAttemptActorId(); + if (attackedByPlayerId != -1) + { + const MWWorld::Ptr playerHitAttemptActor = MWBase::Environment::get().getWorld()->searchPtrViaActorId(attackedByPlayerId); + + if (!playerHitAttemptActor.isInCell()) + player.getClass().getCreatureStats(player).setHitAttemptActorId(-1); + } + // AI and magic effects update for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) { @@ -1328,11 +1338,6 @@ namespace MWMechanics player.getClass().getCreatureStats(player).setHitAttemptActorId(-1); } - const MWWorld::Ptr playerHitAttemptActor = MWBase::Environment::get().getWorld()->searchPtrViaActorId(player.getClass().getCreatureStats(player).getHitAttemptActorId()); - - if (!playerHitAttemptActor.isInCell()) - player.getClass().getCreatureStats(player).setHitAttemptActorId(-1); - if (!iter->first.getClass().getCreatureStats(iter->first).isDead()) { bool cellChanged = MWBase::Environment::get().getWorld()->hasCellChanged(); @@ -1343,7 +1348,7 @@ namespace MWMechanics return; // for now abort update of the old cell when cell changes by teleportation magic effect // a better solution might be to apply cell changes at the end of the frame } - if (MWBase::Environment::get().getMechanicsManager()->isAIActive() && inProcessingRange) + if (aiActive && inProcessingRange) { if (timerUpdateAITargets == 0) { From b9346798c6bef7ec8b06d48853dd415d4e0dda91 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Fri, 21 Sep 2018 22:34:18 +0400 Subject: [PATCH 058/163] Optimize combat music update --- apps/openmw/mwmechanics/actors.cpp | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index e3b1ec791..0cd9912d0 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -1242,21 +1242,24 @@ namespace MWMechanics void Actors::updateCombatMusic () { MWWorld::Ptr player = getPlayer(); - int hostilesCount = 0; // need to know this to play Battle music + const osg::Vec3f playerPos = player.getRefData().getPosition().asVec3(); + bool hasHostiles = false; // need to know this to play Battle music + bool aiActive = MWBase::Environment::get().getMechanicsManager()->isAIActive(); - for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) + if (aiActive) { - if (!iter->first.getClass().getCreatureStats(iter->first).isDead()) + for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) { - bool inProcessingRange = (player.getRefData().getPosition().asVec3() - iter->first.getRefData().getPosition().asVec3()).length2() - <= sqrAiProcessingDistance; + if (iter->first == player) continue; - if (MWBase::Environment::get().getMechanicsManager()->isAIActive() && inProcessingRange) + bool inProcessingRange = (playerPos - iter->first.getRefData().getPosition().asVec3()).length2() <= sqrAiProcessingDistance; + if (inProcessingRange) { - if (iter->first != player) + MWMechanics::CreatureStats& stats = iter->first.getClass().getCreatureStats(iter->first); + if (!stats.isDead() && stats.getAiSequence().isInCombat()) { - MWMechanics::CreatureStats& stats = iter->first.getClass().getCreatureStats(iter->first); - if (stats.getAiSequence().isInCombat() && !stats.isDead()) hostilesCount++; + hasHostiles = true; + break; } } } @@ -1265,13 +1268,13 @@ namespace MWMechanics // check if we still have any player enemies to switch music static int currentMusic = 0; - if (currentMusic != 1 && hostilesCount == 0 && !(player.getClass().getCreatureStats(player).isDead() && + if (currentMusic != 1 && !hasHostiles && !(player.getClass().getCreatureStats(player).isDead() && MWBase::Environment::get().getSoundManager()->isMusicPlaying())) { MWBase::Environment::get().getSoundManager()->playPlaylist(std::string("Explore")); currentMusic = 1; } - else if (currentMusic != 2 && hostilesCount > 0) + else if (currentMusic != 2 && hasHostiles) { MWBase::Environment::get().getSoundManager()->playPlaylist(std::string("Battle")); currentMusic = 2; From 1634284739eb02715ca37c8c95f90695d3206dbb Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Fri, 21 Sep 2018 22:39:47 +0400 Subject: [PATCH 059/163] Cache player's position outside of loops --- 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 0cd9912d0..79622180f 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -1301,6 +1301,7 @@ namespace MWMechanics bool showTorches = MWBase::Environment::get().getWorld()->useTorches(); MWWorld::Ptr player = getPlayer(); + const osg::Vec3f playerPos = player.getRefData().getPosition().asVec3(); /// \todo move update logic to Actor class where appropriate @@ -1321,7 +1322,7 @@ namespace MWMechanics { bool isPlayer = iter->first == player; - float distSqr = (player.getRefData().getPosition().asVec3() - iter->first.getRefData().getPosition().asVec3()).length2(); + float distSqr = (playerPos - 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) // This distance could be made configurable later, but the setting must be marked with a big warning: @@ -1431,7 +1432,7 @@ namespace MWMechanics for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) { const float animationDistance = aiProcessingDistance + 400; // Slightly larger than AI distance so there is time to switch back to the idle animation. - const float distSqr = (player.getRefData().getPosition().asVec3() - iter->first.getRefData().getPosition().asVec3()).length2(); + const float distSqr = (playerPos - iter->first.getRefData().getPosition().asVec3()).length2(); bool isPlayer = iter->first == player; bool inAnimationRange = isPlayer || (animationDistance == 0 || distSqr <= animationDistance*animationDistance); int activeFlag = 1; // Can be changed back to '2' to keep updating bounding boxes off screen (more accurate, but slower) @@ -1520,7 +1521,7 @@ namespace MWMechanics continue; // is the player in range and can they be detected - if ((observer.getRefData().getPosition().asVec3() - player.getRefData().getPosition().asVec3()).length2() <= radius*radius + if ((observer.getRefData().getPosition().asVec3() - playerPos).length2() <= radius*radius && MWBase::Environment::get().getWorld()->getLOS(player, observer)) { if (MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, observer)) @@ -1664,7 +1665,8 @@ namespace MWMechanics void Actors::rest(bool sleep) { float duration = 3600.f / MWBase::Environment::get().getWorld()->getTimeScaleFactor(); - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + const MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + const osg::Vec3f playerPos = player.getRefData().getPosition().asVec3(); for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) { @@ -1674,7 +1676,7 @@ namespace MWMechanics restoreDynamicStats(iter->first, sleep); if ((!iter->first.getRefData().getBaseNode()) || - (player.getRefData().getPosition().asVec3() - iter->first.getRefData().getPosition().asVec3()).length2() > sqrAiProcessingDistance) + (playerPos - iter->first.getRefData().getPosition().asVec3()).length2() > sqrAiProcessingDistance) continue; adjustMagicEffects (iter->first); From b3fd173e007b62071baea8cc11e756adffcb1c68 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 22 Sep 2018 06:51:56 +0400 Subject: [PATCH 060/163] Check if current weapon has health at all in HUD (bug #4648) --- CHANGELOG.md | 1 + apps/openmw/mwgui/windowmanagerimp.cpp | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b45727939..22ff55bb2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -125,6 +125,7 @@ Bug #4633: Sneaking stance affects speed even if the actor is not able to crouch Bug #4641: GetPCJumping is handled incorrectly Bug #4644: %Name should be available for all actors, not just for NPCs + Bug #4648: Hud thinks that throwing weapons have condition Feature #912: Editor: Add missing icons to UniversalId tables Feature #1617: Editor: Enchantment effect record verifier Feature #1645: Casting effects from objects diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 98dbddaff..7fca0058f 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -1383,8 +1383,11 @@ namespace MWGui void WindowManager::setSelectedWeapon(const MWWorld::Ptr& item) { mSelectedWeapon = item; - int durabilityPercent = - static_cast(item.getClass().getItemHealth(item) / static_cast(item.getClass().getItemMaxHealth(item)) * 100); + int durabilityPercent = 100; + if (item.getClass().hasItemHealth(item)) + { + durabilityPercent = static_cast(item.getClass().getItemHealth(item) / static_cast(item.getClass().getItemMaxHealth(item)) * 100); + } mHud->setSelectedWeapon(item, durabilityPercent); mInventoryWindow->setTitle(item.getClass().getName(item)); } From 90d35aaa8ff64cb9d3ef373fbcde56c5dce1f961 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 22 Sep 2018 07:46:47 +0400 Subject: [PATCH 061/163] Handle current health level during levelup (bug #4649) --- CHANGELOG.md | 1 + apps/openmw/mwmechanics/npcstats.cpp | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b45727939..557848bb4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -125,6 +125,7 @@ Bug #4633: Sneaking stance affects speed even if the actor is not able to crouch Bug #4641: GetPCJumping is handled incorrectly Bug #4644: %Name should be available for all actors, not just for NPCs + Bug #4649: Levelup fully restores health Feature #912: Editor: Add missing icons to UniversalId tables Feature #1617: Editor: Enchantment effect record verifier Feature #1645: Casting effects from objects diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index cb9ef5477..47d886e3f 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -298,7 +298,12 @@ void MWMechanics::NpcStats::levelUp() // "When you gain a level, in addition to increasing three primary attributes, your Health // will automatically increase by 10% of your Endurance attribute. If you increased Endurance this level, // the Health increase is calculated from the increased Endurance" - setHealth(getHealth().getBase() + endurance * gmst.find("fLevelUpHealthEndMult")->mValue.getFloat()); + // Note: we should add bonus Health points to current level too. + float healthGain = endurance * gmst.find("fLevelUpHealthEndMult")->mValue.getFloat(); + MWMechanics::DynamicStat health(getHealth()); + health.setBase(getHealth().getBase() + healthGain); + health.setCurrent(std::max(1.f, getHealth().getCurrent() + healthGain)); + setHealth(health); setLevel(getLevel()+1); } From 2ac2d014322546ba3d6e86b27cb2ac41babde0cc Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 22 Sep 2018 15:48:36 +0400 Subject: [PATCH 062/163] Optimize drowning state update --- apps/openmw/mwmechanics/actors.cpp | 27 +++++++++------------------ apps/openmw/mwmechanics/actors.hpp | 4 +--- 2 files changed, 10 insertions(+), 21 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 79622180f..9ea357f56 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -501,12 +501,6 @@ namespace MWMechanics } } - void Actors::updateNpc (const MWWorld::Ptr& ptr, float duration) - { - updateDrowning(ptr, duration); - calculateNpcStatModifiers(ptr, duration); - } - void Actors::adjustMagicEffects (const MWWorld::Ptr& creature) { CreatureStats& creatureStats = creature.getClass().getCreatureStats (creature); @@ -926,13 +920,8 @@ namespace MWMechanics return ctrl->isSneaking(); } - void Actors::updateDrowning(const MWWorld::Ptr& ptr, float duration) + void Actors::updateDrowning(const MWWorld::Ptr& ptr, float duration, bool isKnockedOut, bool isPlayer) { - PtrActorMap::iterator it = mActors.find(ptr); - if (it == mActors.end()) - return; - CharacterController* ctrl = it->second->getCharacterController(); - NpcStats &stats = ptr.getClass().getNpcStats(ptr); // When npc stats are just initialized, mTimeToStartDrowning == -1 and we should get value from GMST @@ -940,9 +929,10 @@ namespace MWMechanics if (stats.getTimeToStartDrowning() == -1.f) stats.setTimeToStartDrowning(fHoldBreathTime); - if (ptr.getClass().isNpc() && stats.getTimeToStartDrowning() < fHoldBreathTime / 2) + if (stats.getTimeToStartDrowning() < fHoldBreathTime / 2) { - if(ptr != MWMechanics::getPlayer() ) { + if(!isPlayer) + { MWMechanics::AiSequence& seq = ptr.getClass().getCreatureStats(ptr).getAiSequence(); if(seq.getTypeId() != MWMechanics::AiPackage::TypeIdBreathe) //Only add it once seq.stack(MWMechanics::AiBreathe(), ptr); @@ -950,7 +940,7 @@ namespace MWMechanics } MWBase::World *world = MWBase::Environment::get().getWorld(); - bool knockedOutUnderwater = (ctrl->isKnockedOut() && world->isUnderwater(ptr.getCell(), osg::Vec3f(ptr.getRefData().getPosition().asVec3()))); + bool knockedOutUnderwater = (isKnockedOut && world->isUnderwater(ptr.getCell(), osg::Vec3f(ptr.getRefData().getPosition().asVec3()))); if((world->isSubmerged(ptr) || knockedOutUnderwater) && stats.getMagicEffects().get(ESM::MagicEffect::WaterBreathing).getMagnitude() == 0) { @@ -965,7 +955,7 @@ namespace MWMechanics stats.setTimeToStartDrowning(timeLeft); } - bool godmode = ptr == MWMechanics::getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState(); + bool godmode = isPlayer && MWBase::Environment::get().getWorld()->getGodModeState(); if(timeLeft == 0.0f && !godmode) { @@ -980,7 +970,7 @@ namespace MWMechanics if(!sndmgr->getSoundPlaying(ptr, "drown")) sndmgr->playSound3D(ptr, "drown", 1.0f, 1.0f); - if(ptr == world->getPlayerPtr()) + if(isPlayer) MWBase::Environment::get().getWindowManager()->activateHitOverlay(false); } } @@ -1406,7 +1396,8 @@ namespace MWMechanics if(iter->first.getTypeName() == typeid(ESM::NPC).name()) { - updateNpc(iter->first, duration); + updateDrowning(iter->first, duration, iter->second->getCharacterController()->isKnockedOut(), isPlayer); + calculateNpcStatModifiers(iter->first, duration); if (timerUpdateEquippedLight == 0) updateEquippedLight(iter->first, updateEquippedLightInterval, showTorches); diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index 8c94ce45f..a1e0e511d 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -28,8 +28,6 @@ namespace MWMechanics void addBoundItem (const std::string& itemId, const MWWorld::Ptr& actor); void removeBoundItem (const std::string& itemId, const MWWorld::Ptr& actor); - void updateNpc(const MWWorld::Ptr &ptr, float duration); - void adjustMagicEffects (const MWWorld::Ptr& creature); void calculateDynamicStats (const MWWorld::Ptr& ptr); @@ -39,7 +37,7 @@ namespace MWMechanics void calculateRestoration (const MWWorld::Ptr& ptr, float duration); - void updateDrowning (const MWWorld::Ptr& ptr, float duration); + void updateDrowning (const MWWorld::Ptr& ptr, float duration, bool isKnockedOut, bool isPlayer); void updateEquippedLight (const MWWorld::Ptr& ptr, float duration, bool mayEquip); From cd60d4fdf093b99e9ed30729e2e98c5949423ae4 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 22 Sep 2018 15:57:58 +0400 Subject: [PATCH 063/163] Store character controller in the variable make code less bulk --- apps/openmw/mwmechanics/actors.cpp | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 9ea357f56..92f2ba34d 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -1311,6 +1311,7 @@ namespace MWMechanics for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) { bool isPlayer = iter->first == player; + CharacterController* ctrl = iter->second->getCharacterController(); float distSqr = (playerPos - 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 @@ -1320,7 +1321,7 @@ namespace MWMechanics bool inProcessingRange = distSqr <= sqrAiProcessingDistance; if (isPlayer) - iter->second->getCharacterController()->setAttackingOrSpell(MWBase::Environment::get().getWorld()->getPlayer().getAttackingOrSpell()); + ctrl->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. if (iter->first != player && (iter->first.getClass().getCreatureStats(iter->first).isDead() @@ -1380,7 +1381,7 @@ namespace MWMechanics } } - iter->second->getCharacterController()->setHeadTrackTarget(headTrackTarget); + ctrl->setHeadTrackTarget(headTrackTarget); } if (iter->first.getClass().isNpc() && iter->first != player) @@ -1390,13 +1391,13 @@ namespace MWMechanics { CreatureStats &stats = iter->first.getClass().getCreatureStats(iter->first); if (isConscious(iter->first)) - stats.getAiSequence().execute(iter->first, *iter->second->getCharacterController(), duration); + stats.getAiSequence().execute(iter->first, *ctrl, duration); } } if(iter->first.getTypeName() == typeid(ESM::NPC).name()) { - updateDrowning(iter->first, duration, iter->second->getCharacterController()->isKnockedOut(), isPlayer); + updateDrowning(iter->first, duration, ctrl->isKnockedOut(), isPlayer); calculateNpcStatModifiers(iter->first, duration); if (timerUpdateEquippedLight == 0) @@ -1437,22 +1438,24 @@ namespace MWMechanics inAnimationRange = true; active = std::max(1, active); } - iter->second->getCharacterController()->setActive(active); + + CharacterController* ctrl = iter->second->getCharacterController(); + ctrl->setActive(active); if (!inAnimationRange) continue; if (iter->first.getClass().getCreatureStats(iter->first).isParalyzed()) - iter->second->getCharacterController()->skipAnim(); + ctrl->skipAnim(); // Handle player last, in case a cell transition occurs by casting a teleportation spell // (would invalidate the iterator) if (iter->first == getPlayer()) { - playerCharacter = iter->second->getCharacterController(); + playerCharacter = ctrl; continue; } - iter->second->getCharacterController()->update(duration); + ctrl->update(duration); } if (playerCharacter) From c50ee22772cb7cb4e8229fc5e0e73ff46df85ad2 Mon Sep 17 00:00:00 2001 From: Sergey Fukanchik Date: Sun, 23 Sep 2018 01:52:56 +0300 Subject: [PATCH 064/163] Wrap up World::canRest() implementation by moving enemiesNearby() from InputManager::rest() to World::canRest(). --- apps/openmw/mwbase/world.hpp | 16 ++++++++++------ apps/openmw/mwgui/waitdialog.cpp | 11 ++++++++--- apps/openmw/mwinput/inputmanagerimp.cpp | 4 ---- apps/openmw/mwworld/worldimp.cpp | 11 +++++++---- apps/openmw/mwworld/worldimp.hpp | 2 +- 5 files changed, 26 insertions(+), 18 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 72c4c3a2a..4e9e3f4a9 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -438,12 +438,16 @@ namespace MWBase virtual void enableActorCollision(const MWWorld::Ptr& actor, bool enable) = 0; - virtual int canRest() = 0; - ///< check if the player is allowed to rest \n - /// 0 - yes \n - /// 1 - only waiting \n - /// 2 - player is underwater \n - /// 3 - enemies are nearby (not implemented) + enum RestPermitted + { + Rest_Allowed = 0, + Rest_OnlyWaiting = 1, + Rest_PlayerIsUnderwater = 2, + Rest_EnemiesAreNearby = 3 + }; + + virtual RestPermitted canRest() = 0; + ///< check if the player is allowed to rest /// \todo Probably shouldn't be here virtual MWRender::Animation* getAnimation(const MWWorld::Ptr &ptr) = 0; diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index c5a06a12c..37cc9a59f 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -83,7 +83,7 @@ namespace MWGui void WaitDialog::setPtr(const MWWorld::Ptr &ptr) { - setCanRest(!ptr.isEmpty() || MWBase::Environment::get().getWorld ()->canRest () == 0); + setCanRest(!ptr.isEmpty() || MWBase::Environment::get().getWorld ()->canRest () == MWBase::World::Rest_Allowed); if (mUntilHealedButton->getVisible()) MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mUntilHealedButton); @@ -120,9 +120,14 @@ namespace MWGui MWBase::Environment::get().getWindowManager()->popGuiMode (); } - int canRest = MWBase::Environment::get().getWorld ()->canRest (); + MWBase::World::RestPermitted canRest = MWBase::Environment::get().getWorld ()->canRest (); - if (canRest == 2) + if (canRest == MWBase::World::Rest_EnemiesAreNearby) + { + MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage2}"); + MWBase::Environment::get().getWindowManager()->popGuiMode (); + } + else if (canRest == MWBase::World::Rest_PlayerIsUnderwater) { // resting underwater or mid-air not allowed MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage1}"); diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index ba9370122..101b4f2f7 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -1029,10 +1029,6 @@ namespace MWInput if (!MWBase::Environment::get().getWindowManager()->getRestEnabled () || MWBase::Environment::get().getWindowManager()->isGuiMode ()) return; - if(mPlayer->enemiesNearby()) {//Check if in combat - MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage2}"); //Nope, - return; - } MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_Rest); //Open rest GUI } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 2afcdffc8..458ff3a01 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2297,7 +2297,7 @@ namespace MWWorld applyLoopingParticles(player); } - int World::canRest () + World::RestPermitted World::canRest () { CellStore *currentCell = mWorldScene->getCurrentCell(); @@ -2309,13 +2309,16 @@ namespace MWWorld if (!actor) throw std::runtime_error("can't find player"); + if(mPlayer->enemiesNearby()) + return Rest_EnemiesAreNearby; + if ((actor->getCollisionMode() && !mPhysics->isOnSolidGround(player)) || isUnderwater(currentCell, playerPos) || isWalkingOnWater(player)) - return 2; + return Rest_PlayerIsUnderwater; if((currentCell->getCell()->mData.mFlags&ESM::Cell::NoSleep) || player.getClass().getNpcStats(player).isWerewolf()) - return 1; + return Rest_OnlyWaiting; - return 0; + return Rest_Allowed; } MWRender::Animation* World::getAnimation(const MWWorld::Ptr &ptr) diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 536a40468..92fb091bf 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -551,7 +551,7 @@ namespace MWWorld void enableActorCollision(const MWWorld::Ptr& actor, bool enable) override; - int canRest() override; + World::RestPermitted canRest() override; ///< check if the player is allowed to rest \n /// 0 - yes \n /// 1 - only waiting \n From 44bcd9b25fa8c94da732de933e2465185d58673c Mon Sep 17 00:00:00 2001 From: Sergey Fukanchik Date: Sun, 23 Sep 2018 02:46:29 +0300 Subject: [PATCH 065/163] Fix tab characters --- apps/openmw/mwgui/waitdialog.cpp | 8 ++++---- apps/openmw/mwworld/worldimp.cpp | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index 37cc9a59f..db92b355c 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -120,14 +120,14 @@ namespace MWGui MWBase::Environment::get().getWindowManager()->popGuiMode (); } - MWBase::World::RestPermitted canRest = MWBase::Environment::get().getWorld ()->canRest (); + MWBase::World::RestPermitted canRest = MWBase::Environment::get().getWorld ()->canRest (); - if (canRest == MWBase::World::Rest_EnemiesAreNearby) + if (canRest == MWBase::World::Rest_EnemiesAreNearby) { MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage2}"); MWBase::Environment::get().getWindowManager()->popGuiMode (); - } - else if (canRest == MWBase::World::Rest_PlayerIsUnderwater) + } + else if (canRest == MWBase::World::Rest_PlayerIsUnderwater) { // resting underwater or mid-air not allowed MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage1}"); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 458ff3a01..1b3e3c3ef 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2309,8 +2309,8 @@ namespace MWWorld if (!actor) throw std::runtime_error("can't find player"); - if(mPlayer->enemiesNearby()) - return Rest_EnemiesAreNearby; + if(mPlayer->enemiesNearby()) + return Rest_EnemiesAreNearby; if ((actor->getCollisionMode() && !mPhysics->isOnSolidGround(player)) || isUnderwater(currentCell, playerPos) || isWalkingOnWater(player)) return Rest_PlayerIsUnderwater; From 52da65b776948f0119467add6072a41982993f1e Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 23 Sep 2018 03:28:30 +0300 Subject: [PATCH 066/163] Fix issues with sun specularity (bug #4527) --- CHANGELOG.md | 1 + apps/openmw/mwworld/weather.cpp | 11 +++++++---- files/shaders/water_fragment.glsl | 2 +- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b45727939..c4c446d72 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -94,6 +94,7 @@ Bug #4503: Cast and ExplodeSpell commands increase alteration skill Bug #4510: Division by zero in MWMechanics::CreatureStats::setAttribute Bug #4519: Knockdown does not discard movement in the 1st-person mode + Bug #4527: Sun renders on water shader in some situations where it shouldn't Bug #4531: Movement does not reset idle animations Bug #4532: Underwater sfx isn't tied to 3rd person camera Bug #4539: Paper Doll is affected by GUI scaling diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index 2a9e8d7cc..ac885f429 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -752,12 +752,15 @@ void WeatherManager::update(float duration, bool paused, const TimeStamp& time, float underwaterFog = mUnderwaterFog.getValue(time.getHour(), mTimeSettings, "Fog"); float peakHour = mSunriseTime + (mSunsetTime - mSunriseTime) / 2; + float glareFade = 1.f; if (time.getHour() < mSunriseTime || time.getHour() > mSunsetTime) - mRendering.getSkyManager()->setGlareTimeOfDayFade(0); + glareFade = 0.f; else if (time.getHour() < peakHour) - mRendering.getSkyManager()->setGlareTimeOfDayFade(1 - (peakHour - time.getHour()) / (peakHour - mSunriseTime)); + glareFade -= (peakHour - time.getHour()) / (peakHour - mSunriseTime); else - mRendering.getSkyManager()->setGlareTimeOfDayFade(1 - (time.getHour() - peakHour) / (mSunsetTime - peakHour)); + glareFade -= (time.getHour() - peakHour) / (mSunsetTime - peakHour); + + mRendering.getSkyManager()->setGlareTimeOfDayFade(glareFade); mRendering.getSkyManager()->setMasserState(mMasser.calculateState(time)); mRendering.getSkyManager()->setSecundaState(mSecunda.calculateState(time)); @@ -765,7 +768,7 @@ void WeatherManager::update(float duration, bool paused, const TimeStamp& time, mRendering.configureFog(mResult.mFogDepth, underwaterFog, mResult.mDLFogFactor, mResult.mDLFogOffset/100.0f, mResult.mFogColor); mRendering.setAmbientColour(mResult.mAmbientColor); - mRendering.setSunColour(mResult.mSunColor, mResult.mSunColor * mResult.mGlareView); + mRendering.setSunColour(mResult.mSunColor, mResult.mSunColor * mResult.mGlareView * glareFade); mRendering.getSkyManager()->setWeather(mResult); diff --git a/files/shaders/water_fragment.glsl b/files/shaders/water_fragment.glsl index ee91df75f..4c4a253f1 100644 --- a/files/shaders/water_fragment.glsl +++ b/files/shaders/water_fragment.glsl @@ -286,6 +286,6 @@ void main(void) #if REFRACTION gl_FragData[0].w = 1.0; #else - gl_FragData[0].w = clamp(fresnel*6.0 + specular, 0.0, 1.0); //clamp(fresnel*2.0 + specular, 0.0, 1.0); + gl_FragData[0].w = clamp(fresnel*6.0 + specular * gl_LightSource[0].specular.w, 0.0, 1.0); //clamp(fresnel*2.0 + specular * gl_LightSource[0].specular.w, 0.0, 1.0); #endif } From f2e11e6def966680380267ab84169b3670e66237 Mon Sep 17 00:00:00 2001 From: Sergey Fukanchik Date: Sun, 23 Sep 2018 04:06:29 +0300 Subject: [PATCH 067/163] Fix MSVC C4596 illegal qualified name --- apps/openmw/mwworld/worldimp.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 92fb091bf..5c3eb30b3 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -551,7 +551,7 @@ namespace MWWorld void enableActorCollision(const MWWorld::Ptr& actor, bool enable) override; - World::RestPermitted canRest() override; + RestPermitted canRest() override; ///< check if the player is allowed to rest \n /// 0 - yes \n /// 1 - only waiting \n From f00f35ba86f719e2fa7615a3aab9e402acaa27b9 Mon Sep 17 00:00:00 2001 From: Sergey Fukanchik Date: Sun, 23 Sep 2018 06:25:20 +0300 Subject: [PATCH 068/163] Add const to canRest() --- apps/openmw/mwbase/world.hpp | 2 +- apps/openmw/mwworld/worldimp.cpp | 2 +- apps/openmw/mwworld/worldimp.hpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 4e9e3f4a9..330babf09 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -446,7 +446,7 @@ namespace MWBase Rest_EnemiesAreNearby = 3 }; - virtual RestPermitted canRest() = 0; + virtual RestPermitted canRest() const = 0; ///< check if the player is allowed to rest /// \todo Probably shouldn't be here diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 1b3e3c3ef..9b701d17b 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2297,7 +2297,7 @@ namespace MWWorld applyLoopingParticles(player); } - World::RestPermitted World::canRest () + World::RestPermitted World::canRest () const { CellStore *currentCell = mWorldScene->getCurrentCell(); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 5c3eb30b3..e694d9d84 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -551,7 +551,7 @@ namespace MWWorld void enableActorCollision(const MWWorld::Ptr& actor, bool enable) override; - RestPermitted canRest() override; + RestPermitted canRest() const override; ///< check if the player is allowed to rest \n /// 0 - yes \n /// 1 - only waiting \n From 92259c6cca4987d023de675b38781c98cd01db15 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 23 Sep 2018 22:30:06 +0300 Subject: [PATCH 069/163] Discard count arguments for AddSoulGem and RemoveSpell (bug #3762) --- CHANGELOG.md | 1 + components/compiler/extensions0.cpp | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b45727939..41db3b0eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ Bug #3591: Angled hit distance too low Bug #3629: DB assassin attack never triggers creature spawning Bug #3681: OpenMW-CS: Clicking Scripts in Preferences spawns many color pickers + Bug #3762: AddSoulGem and RemoveSpell redundant count arguments break script execution Bug #3788: GetPCInJail and GetPCTraveling do not work as in vanilla Bug #3836: Script fails to compile when command argument contains "\n" Bug #3876: Landscape texture painting is misaligned diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index 7638d0f78..9b74377f2 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -276,7 +276,7 @@ namespace Compiler extensions.registerInstruction ("gotojail", "", opcodeGoToJail); extensions.registerFunction ("getlocked", 'l', "", opcodeGetLocked, opcodeGetLockedExplicit); extensions.registerFunction ("geteffect", 'l', "S", opcodeGetEffect, opcodeGetEffectExplicit); - extensions.registerInstruction ("addsoulgem", "cc", opcodeAddSoulGem, opcodeAddSoulGemExplicit); + extensions.registerInstruction ("addsoulgem", "ccz", opcodeAddSoulGem, opcodeAddSoulGemExplicit); extensions.registerInstruction ("removesoulgem", "c/l", opcodeRemoveSoulGem, opcodeRemoveSoulGemExplicit); extensions.registerInstruction ("drop", "cl", opcodeDrop, opcodeDropExplicit); extensions.registerInstruction ("dropsoulgem", "c", opcodeDropSoulGem, opcodeDropSoulGemExplicit); @@ -463,7 +463,7 @@ namespace Compiler extensions.registerInstruction ("modpccrimelevel", "f", opcodeModPCCrimeLevel); extensions.registerInstruction ("addspell", "cz", opcodeAddSpell, opcodeAddSpellExplicit); - extensions.registerInstruction ("removespell", "c", opcodeRemoveSpell, + extensions.registerInstruction ("removespell", "cz", opcodeRemoveSpell, opcodeRemoveSpellExplicit); extensions.registerInstruction ("removespelleffects", "c", opcodeRemoveSpellEffects, opcodeRemoveSpellEffectsExplicit); From b2a52a5b6c1ba97ee3d5bab99cfb66a675e3ad78 Mon Sep 17 00:00:00 2001 From: Sergey Fukanchik Date: Mon, 24 Sep 2018 14:59:10 +0300 Subject: [PATCH 070/163] Change 'after function'-style doxygen comment to normal. --- apps/openmw/mwbase/world.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 330babf09..f44e6593b 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -446,8 +446,8 @@ namespace MWBase Rest_EnemiesAreNearby = 3 }; + /// check if the player is allowed to rest virtual RestPermitted canRest() const = 0; - ///< check if the player is allowed to rest /// \todo Probably shouldn't be here virtual MWRender::Animation* getAnimation(const MWWorld::Ptr &ptr) = 0; From 657d95a0bedc63e252139a28c6275e3b8ed86c27 Mon Sep 17 00:00:00 2001 From: Sergey Fukanchik Date: Mon, 24 Sep 2018 15:30:30 +0300 Subject: [PATCH 071/163] Paperwork for the pull request #1935 --- AUTHORS.md | 1 + CHANGELOG.md | 1 + 2 files changed, 2 insertions(+) diff --git a/AUTHORS.md b/AUTHORS.md index 5320a2462..074081876 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -148,6 +148,7 @@ Programmers Scott Howard scrawl Sebastian Wick (swick) + Sergey Fukanchik Sergey Shambir ShadowRadiance Siimacore diff --git a/CHANGELOG.md b/CHANGELOG.md index b45727939..7083fcdb7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -164,6 +164,7 @@ Task #4621: Optimize combat AI Task #4643: Revise editor record verifying functionality Task #4645: Use constants instead of widely used magic numbers + Task #4652: Move call to enemiesNearby() from InputManager::rest() to World::canRest() 0.44.0 ------ From 713330351bf1d8fa0867860266fce2de374e48d5 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 23 Sep 2018 00:08:16 +0300 Subject: [PATCH 072/163] Experimental animation regression fixes Don't unnecessarily start movement and jump animations from loop start Don't play movement animation until jumping animation finishes --- apps/openmw/mwmechanics/character.cpp | 104 +++++++++++++------------- apps/openmw/mwmechanics/character.hpp | 2 +- 2 files changed, 55 insertions(+), 51 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 749f908d2..8f14e0331 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -351,64 +351,69 @@ void CharacterController::refreshHitRecoilAnims(CharacterState& idle) idle = CharState_None; } -void CharacterController::refreshJumpAnims(const WeaponInfo* weap, JumpingState jump, CharacterState& idle, bool force) +void CharacterController::refreshJumpAnims(const WeaponInfo* weap, JumpingState jump, CharacterState& idle, CharacterState& movement, bool force) { - if(force || jump != mJumpState) + if (!force && jump == mJumpState && movement == CharState_None) + return; + + if (jump != JumpState_None) { - if (jump != JumpState_None) - idle = CharState_None; + idle = CharState_None; + movement = CharState_None; + } - bool startAtLoop = (jump == mJumpState); - mJumpState = jump; - - std::string jumpAnimName; - MWRender::Animation::BlendMask jumpmask = MWRender::Animation::BlendMask_All; - if(mJumpState != JumpState_None) + std::string jumpAnimName; + MWRender::Animation::BlendMask jumpmask = MWRender::Animation::BlendMask_All; + if (jump != JumpState_None) + { + jumpAnimName = "jump"; + if(weap != sWeaponTypeListEnd) { - jumpAnimName = "jump"; - if(weap != sWeaponTypeListEnd) + jumpAnimName += weap->shortgroup; + if(!mAnimation->hasAnimation(jumpAnimName)) { - jumpAnimName += weap->shortgroup; - if(!mAnimation->hasAnimation(jumpAnimName)) - { - jumpmask = MWRender::Animation::BlendMask_LowerBody; - jumpAnimName = "jump"; + jumpmask = MWRender::Animation::BlendMask_LowerBody; + jumpAnimName = "jump"; - // Since we apply movement only for lower body, do not reset idle animations. - // For upper body there will be idle animation. - if (idle == CharState_None) - idle = CharState_Idle; + // Since we apply movement only for lower body, do not reset idle animations. + // For upper body there will be idle animation. + if (idle == CharState_None) + idle = CharState_Idle; - // For crossbow animations use 1h ones as fallback - if (mWeaponType == WeapType_Crossbow) - jumpAnimName += "1h"; - } + // For crossbow animations use 1h ones as fallback + if (mWeaponType == WeapType_Crossbow) + jumpAnimName += "1h"; } } + } - if (!mCurrentJump.empty()) - { - mAnimation->disable(mCurrentJump); - mCurrentJump.clear(); - } + if (!force && jump == mJumpState) + return; - if(mJumpState == JumpState_InAir) + mJumpState = jump; + + if (!mCurrentJump.empty()) + { + mAnimation->disable(mCurrentJump); + mCurrentJump.clear(); + } + + if(mJumpState == JumpState_InAir) + { + if (mAnimation->hasAnimation(jumpAnimName)) { - if (mAnimation->hasAnimation(jumpAnimName)) - { - mAnimation->play(jumpAnimName, Priority_Jump, jumpmask, false, - 1.0f, (startAtLoop?"loop start":"start"), "stop", 0.0f, ~0ul); - mCurrentJump = jumpAnimName; - } + mAnimation->play(jumpAnimName, Priority_Jump, jumpmask, false, + 1.0f, "start", "stop", 0.f, ~0ul); + mCurrentJump = jumpAnimName; } - else if (mJumpState == JumpState_Landing) + } + else if (mJumpState == JumpState_Landing) + { + if (mAnimation->hasAnimation(jumpAnimName)) { - if (mAnimation->hasAnimation(jumpAnimName)) - { - mAnimation->play(jumpAnimName, Priority_Jump, jumpmask, true, - 1.0f, "loop stop", "stop", 0.0f, 0); - mCurrentJump = jumpAnimName; - } + mAnimation->play(jumpAnimName, Priority_Jump, jumpmask, true, + 1.0f, "loop stop", "stop", 0.0f, 0); + mCurrentJump = jumpAnimName; } } } @@ -494,10 +499,9 @@ void CharacterController::refreshMovementAnims(const WeaponInfo* weap, Character } // If we're playing the same animation, start it from the point it ended - bool sameAnim = (movementAnimName == mCurrentMovement); - float startPoint = 0.f; - if (sameAnim) - mAnimation->getInfo(mCurrentMovement, &startPoint); + float startpoint = 0.f; + if (!mCurrentMovement.empty() && movementAnimName == mCurrentMovement) + mAnimation->getInfo(mCurrentMovement, &startpoint); mMovementAnimationControlled = true; @@ -546,7 +550,7 @@ void CharacterController::refreshMovementAnims(const WeaponInfo* weap, Character } mAnimation->play(mCurrentMovement, Priority_Movement, movemask, false, - 1.f, (!sameAnim ? "start" : "loop start"), "stop", startPoint, ~0ul, true); + 1.f, "start", "stop", startpoint, ~0ul, true); } } } @@ -626,7 +630,7 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat if (!mPtr.getClass().hasInventoryStore(mPtr)) weap = sWeaponTypeListEnd; - refreshJumpAnims(weap, jump, idle, force); + refreshJumpAnims(weap, jump, idle, movement, force); refreshMovementAnims(weap, movement, idle, force); // idle handled last as it can depend on the other states diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 8b3c68048..1db497828 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -214,7 +214,7 @@ class CharacterController : public MWRender::Animation::TextKeyListener void refreshCurrentAnims(CharacterState idle, CharacterState movement, JumpingState jump, bool force=false); void refreshHitRecoilAnims(CharacterState& idle); - void refreshJumpAnims(const WeaponInfo* weap, JumpingState jump, CharacterState& idle, bool force=false); + void refreshJumpAnims(const WeaponInfo* weap, JumpingState jump, CharacterState& idle, CharacterState& movement, bool force=false); void refreshMovementAnims(const WeaponInfo* weap, CharacterState movement, CharacterState& idle, bool force=false); void refreshIdleAnims(const WeaponInfo* weap, CharacterState idle, bool force=false); From 56ef11b023299b64e6177c31a3af832b3b274d8c Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 23 Sep 2018 13:55:49 +0300 Subject: [PATCH 073/163] Reset sneak and swim idle animations when moving --- apps/openmw/mwmechanics/character.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 8f14e0331..c437a21d5 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -2168,12 +2168,12 @@ void CharacterController::update(float duration) if(mAnimQueue.empty() || inwater || sneak) { // Note: turning animations should not interrupt idle ones - if (inwater) + if (movestate != CharState_None && !isTurning()) + idlestate = CharState_None; + else if (inwater) idlestate = CharState_IdleSwim; else if (sneak && !inJump) idlestate = CharState_IdleSneak; - else if (movestate != CharState_None && !isTurning()) - idlestate = CharState_None; else idlestate = CharState_Idle; } From 77fb4d6dd2d8ea589e8befe55a5075fdfbad2b0c Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 23 Sep 2018 14:06:04 +0300 Subject: [PATCH 074/163] Make sure the idle animations are reset while jumping --- apps/openmw/mwmechanics/character.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index c437a21d5..27170d6a8 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -353,7 +353,7 @@ void CharacterController::refreshHitRecoilAnims(CharacterState& idle) void CharacterController::refreshJumpAnims(const WeaponInfo* weap, JumpingState jump, CharacterState& idle, CharacterState& movement, bool force) { - if (!force && jump == mJumpState && movement == CharState_None) + if (!force && jump == mJumpState && idle == CharState_None && movement == CharState_None) return; if (jump != JumpState_None) From d2cad229f897cfb1657095ac362708360e6bc688 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Tue, 25 Sep 2018 17:09:54 +0300 Subject: [PATCH 075/163] Ugly hack: don't reset player idle and movement animations in first person view --- apps/openmw/mwmechanics/character.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 27170d6a8..90f90e9e2 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -356,7 +356,7 @@ void CharacterController::refreshJumpAnims(const WeaponInfo* weap, JumpingState if (!force && jump == mJumpState && idle == CharState_None && movement == CharState_None) return; - if (jump != JumpState_None) + if (jump != JumpState_None && !(mPtr == MWMechanics::getPlayer() && MWBase::Environment::get().getWorld()->isFirstPerson())) // FIXME { idle = CharState_None; movement = CharState_None; From bce8de5fe9332d54221349648a4d1a7788ac7cc2 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Mon, 24 Sep 2018 14:09:24 +0400 Subject: [PATCH 076/163] Respect selected encoding in the content selector (bug #4467) --- CHANGELOG.md | 1 + apps/launcher/datafilespage.cpp | 4 +++- apps/opencs/editor.cpp | 5 +++-- apps/opencs/view/doc/filedialog.cpp | 5 +++++ apps/opencs/view/doc/filedialog.hpp | 1 + components/contentselector/view/contentselector.cpp | 7 ++++++- components/contentselector/view/contentselector.hpp | 1 + 7 files changed, 20 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6283d3f29..392bcf084 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -80,6 +80,7 @@ Bug #4460: Script function "Equip" doesn't bypass beast restrictions 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 #4467: Content selector: cyrillic characters are decoded incorrectly in plugin descriptions 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 diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index 7b703a924..6f2389de3 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -36,6 +36,8 @@ Launcher::DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, Config: ui.setupUi (this); setObjectName ("DataFilesPage"); mSelector = new ContentSelectorView::ContentSelector (ui.contentSelectorWidget); + const QString encoding = mGameSettings.value("encoding", "win1252"); + mSelector->setEncoding(encoding); mProfileDialog = new TextInputDialog(tr("New Content List"), tr("Content List name:"), this); @@ -357,4 +359,4 @@ void Launcher::DataFilesPage::reloadCells(QStringList selectedFiles) QStringList cellNamesList = QStringList::fromSet(cellNameLoader.getCellNames(selectedFiles)); std::sort(cellNamesList.begin(), cellNamesList.end()); emit signalLoadedCellsChanged(cellNamesList); -} \ No newline at end of file +} diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index e733d9924..f29c6aca8 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -108,8 +108,9 @@ std::pair > CS::Editor::readConfi mCfgMgr.readConfiguration(variables, desc, quiet); - mDocumentManager.setEncoding ( - ToUTF8::calculateEncoding (variables["encoding"].as().toStdString())); + const std::string encoding = variables["encoding"].as().toStdString(); + mDocumentManager.setEncoding (ToUTF8::calculateEncoding (encoding)); + mFileDialog.setEncoding (QString::fromUtf8(encoding.c_str())); mDocumentManager.setResourceDir (mResources = variables["resources"].as().toStdString()); diff --git a/apps/opencs/view/doc/filedialog.cpp b/apps/opencs/view/doc/filedialog.cpp index b6f4aaec3..3c5f20202 100644 --- a/apps/opencs/view/doc/filedialog.cpp +++ b/apps/opencs/view/doc/filedialog.cpp @@ -33,6 +33,11 @@ void CSVDoc::FileDialog::addFiles(const QString &path) mSelector->addFiles(path); } +void CSVDoc::FileDialog::setEncoding(const QString &encoding) +{ + mSelector->setEncoding(encoding); +} + void CSVDoc::FileDialog::clearFiles() { mSelector->clearFiles(); diff --git a/apps/opencs/view/doc/filedialog.hpp b/apps/opencs/view/doc/filedialog.hpp index ca6145b9c..69acfac3d 100644 --- a/apps/opencs/view/doc/filedialog.hpp +++ b/apps/opencs/view/doc/filedialog.hpp @@ -42,6 +42,7 @@ namespace CSVDoc void showDialog (ContentAction action); void addFiles (const QString &path); + void setEncoding (const QString &encoding); void clearFiles (); QString filename() const; diff --git a/components/contentselector/view/contentselector.cpp b/components/contentselector/view/contentselector.cpp index 5e16064f4..595be9f44 100644 --- a/components/contentselector/view/contentselector.cpp +++ b/components/contentselector/view/contentselector.cpp @@ -111,6 +111,11 @@ void ContentSelectorView::ContentSelector::clearCheckStates() mContentModel->uncheckAll(); } +void ContentSelectorView::ContentSelector::setEncoding(const QString &encoding) +{ + mContentModel->setEncoding(encoding); +} + void ContentSelectorView::ContentSelector::setContentList(const QStringList &list) { if (list.isEmpty()) @@ -239,4 +244,4 @@ void ContentSelectorView::ContentSelector::slotUncheckMultiSelectedItems() void ContentSelectorView::ContentSelector::slotCheckMultiSelectedItems() { setCheckStateForMultiSelectedItems(true); -} \ No newline at end of file +} diff --git a/components/contentselector/view/contentselector.hpp b/components/contentselector/view/contentselector.hpp index 323f926ed..9f9ad886c 100644 --- a/components/contentselector/view/contentselector.hpp +++ b/components/contentselector/view/contentselector.hpp @@ -32,6 +32,7 @@ namespace ContentSelectorView void setProfileContent (const QStringList &fileList); void clearCheckStates(); + void setEncoding (const QString &encoding); void setContentList(const QStringList &list); ContentSelectorModel::ContentFileList selectedFiles() const; From 0176ee389e1af840c2c73c701a865bcf12beffbf Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 25 Sep 2018 17:16:40 +0400 Subject: [PATCH 077/163] Handle length of UTF-8 strings properly in plugin metadata (bug #4653) --- CHANGELOG.md | 1 + apps/opencs/model/world/metadata.cpp | 4 ++-- components/esm/esmreader.hpp | 4 ++-- components/esm/loadtes3.cpp | 8 ++++---- components/esm/loadtes3.hpp | 4 ++-- 5 files changed, 11 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 392bcf084..93592dec4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -129,6 +129,7 @@ Bug #4644: %Name should be available for all actors, not just for NPCs Bug #4648: Hud thinks that throwing weapons have condition Bug #4649: Levelup fully restores health + Bug #4653: Length of non-ASCII strings is handled incorrectly in ESM reader Feature #912: Editor: Add missing icons to UniversalId tables Feature #1617: Editor: Enchantment effect record verifier Feature #1645: Casting effects from objects diff --git a/apps/opencs/model/world/metadata.cpp b/apps/opencs/model/world/metadata.cpp index 960fdc9e4..b2fa3487c 100644 --- a/apps/opencs/model/world/metadata.cpp +++ b/apps/opencs/model/world/metadata.cpp @@ -14,8 +14,8 @@ void CSMWorld::MetaData::blank() void CSMWorld::MetaData::load (ESM::ESMReader& esm) { mFormat = esm.getHeader().mFormat; - mAuthor = esm.getHeader().mData.author.toString(); - mDescription = esm.getHeader().mData.desc.toString(); + mAuthor = esm.getHeader().mData.author; + mDescription = esm.getHeader().mData.desc; } void CSMWorld::MetaData::save (ESM::ESMWriter& esm) const diff --git a/components/esm/esmreader.hpp b/components/esm/esmreader.hpp index 4772aeb6f..6e84fa7d4 100644 --- a/components/esm/esmreader.hpp +++ b/components/esm/esmreader.hpp @@ -33,8 +33,8 @@ public: int getVer() const { return mHeader.mData.version; } int getRecordCount() const { return mHeader.mData.records; } float getFVer() const { return (mHeader.mData.version == VER_12) ? 1.2f : 1.3f; } - const std::string getAuthor() const { return mHeader.mData.author.toString(); } - const std::string getDesc() const { return mHeader.mData.desc.toString(); } + const std::string getAuthor() const { return mHeader.mData.author; } + const std::string getDesc() const { return mHeader.mData.desc; } const std::vector &getGameFiles() const { return mHeader.mMaster; } const Header& getHeader() const { return mHeader; } int getFormat() const; diff --git a/components/esm/loadtes3.cpp b/components/esm/loadtes3.cpp index b16145467..d953f1dc2 100644 --- a/components/esm/loadtes3.cpp +++ b/components/esm/loadtes3.cpp @@ -32,8 +32,8 @@ void ESM::Header::load (ESMReader &esm) esm.getSubHeader(); esm.getT(mData.version); esm.getT(mData.type); - mData.author.assign( esm.getString(mData.author.data_size()) ); - mData.desc.assign( esm.getString(mData.desc.data_size()) ); + mData.author.assign( esm.getString(32) ); + mData.desc.assign( esm.getString(256) ); esm.getT(mData.records); } @@ -73,8 +73,8 @@ void ESM::Header::save (ESMWriter &esm) esm.startSubRecord("HEDR"); esm.writeT(mData.version); esm.writeT(mData.type); - esm.writeFixedSizeString(mData.author.toString(), mData.author.data_size()); - esm.writeFixedSizeString(mData.desc.toString(), mData.desc.data_size()); + esm.writeFixedSizeString(mData.author, 32); + esm.writeFixedSizeString(mData.desc, 256); esm.writeT(mData.records); esm.endRecord("HEDR"); diff --git a/components/esm/loadtes3.hpp b/components/esm/loadtes3.hpp index 896936cf5..5b26ac7d2 100644 --- a/components/esm/loadtes3.hpp +++ b/components/esm/loadtes3.hpp @@ -21,8 +21,8 @@ namespace ESM */ unsigned int version; int type; // 0=esp, 1=esm, 32=ess (unused) - NAME32 author; // Author's name - NAME256 desc; // File description + std::string author; // Author's name + std::string desc; // File description int records; // Number of records }; From 8af021d729de4b9d348a4aef5de7af6692a5143f Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sun, 23 Sep 2018 22:03:43 +0400 Subject: [PATCH 078/163] Restore dynamic stats for actors in inactive cells (bug #1875) --- CHANGELOG.md | 1 + apps/openmw/mwbase/mechanicsmanager.hpp | 2 ++ apps/openmw/mwbase/world.hpp | 2 ++ apps/openmw/mwmechanics/actors.cpp | 19 ++++++++------- .../mwmechanics/mechanicsmanagerimp.cpp | 8 +++++++ .../mwmechanics/mechanicsmanagerimp.hpp | 2 ++ apps/openmw/mwworld/cells.cpp | 13 ++++++++++ apps/openmw/mwworld/cells.hpp | 1 + apps/openmw/mwworld/cellstore.cpp | 24 +++++++++++++++++++ apps/openmw/mwworld/cellstore.hpp | 2 ++ apps/openmw/mwworld/worldimp.cpp | 4 ++++ apps/openmw/mwworld/worldimp.hpp | 8 +++---- 12 files changed, 72 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6283d3f29..1ef504003 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ 0.45.0 ------ + Bug #1875: Actors in inactive cells don't heal from resting Bug #1990: Sunrise/sunset not set correct Bug #2131: Lustidrike's spell misses the player every time Bug #2222: Fatigue's effect on selling price is backwards diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index fe3fc5721..8137bad95 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -90,6 +90,8 @@ namespace MWBase virtual void setPlayerClass (const ESM::Class& class_) = 0; ///< Set player class to custom class. + virtual void restoreDynamicStats(MWWorld::Ptr actor, bool sleep) = 0; + virtual void rest(bool sleep) = 0; ///< If the player is sleeping or waiting, this should be called every hour. /// @param sleep is the player sleeping or waiting? diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index f44e6593b..e17935abc 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -571,6 +571,8 @@ namespace MWBase virtual bool isPlayerInJail() const = 0; + virtual void rest() = 0; + virtual void setPlayerTraveling(bool traveling) = 0; virtual bool isPlayerTraveling() const = 0; diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 92f2ba34d..7f7355eef 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -545,10 +545,10 @@ namespace MWMechanics void Actors::restoreDynamicStats (const MWWorld::Ptr& ptr, bool sleep) { - if (ptr.getClass().getCreatureStats(ptr).isDead()) + MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats (ptr); + if (stats.isDead()) return; - MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats (ptr); const MWWorld::Store& settings = MWBase::Environment::get().getWorld()->getStore().get(); if (sleep) @@ -565,12 +565,6 @@ namespace MWMechanics stats.setMagicka(stat); } - int endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified (); - - 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(); @@ -582,6 +576,12 @@ namespace MWMechanics float fFatigueReturnMult = settings.find("fFatigueReturnMult")->mValue.getFloat (); float fEndFatigueMult = settings.find("fEndFatigueMult")->mValue.getFloat (); + int endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified (); + + float normalizedEncumbrance = ptr.getClass().getNormalizedEncumbrance(ptr); + if (normalizedEncumbrance > 1) + normalizedEncumbrance = 1; + float x = fFatigueReturnBase + fFatigueReturnMult * (1 - normalizedEncumbrance); x *= fEndFatigueMult * endurance; @@ -1667,7 +1667,8 @@ namespace MWMechanics if (iter->first.getClass().getCreatureStats(iter->first).isDead()) continue; - restoreDynamicStats(iter->first, sleep); + if (!sleep || iter->first == player) + restoreDynamicStats(iter->first, sleep); if ((!iter->first.getRefData().getBaseNode()) || (playerPos - iter->first.getRefData().getPosition().asVec3()).length2() > sqrAiProcessingDistance) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 859b2e522..33357b79a 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -453,9 +453,17 @@ namespace MWMechanics void MechanicsManager::rest(bool sleep) { + if (sleep) + MWBase::Environment::get().getWorld()->rest(); + mActors.rest(sleep); } + void MechanicsManager::restoreDynamicStats(MWWorld::Ptr actor, bool sleep) + { + mActors.restoreDynamicStats(actor, sleep); + } + int MechanicsManager::getHoursToRest() const { return mActors.getHoursToRest(mWatched); diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index af12d4d98..3682e97ce 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -91,6 +91,8 @@ namespace MWMechanics virtual void setPlayerClass (const ESM::Class& class_); ///< Set player class to custom class. + virtual void restoreDynamicStats(MWWorld::Ptr actor, bool sleep); + virtual void rest(bool sleep); ///< If the player is sleeping or waiting, this should be called every hour. /// @param sleep is the player sleeping or waiting? diff --git a/apps/openmw/mwworld/cells.cpp b/apps/openmw/mwworld/cells.cpp index 5cac12b9c..8506cefa3 100644 --- a/apps/openmw/mwworld/cells.cpp +++ b/apps/openmw/mwworld/cells.cpp @@ -151,6 +151,19 @@ MWWorld::CellStore *MWWorld::Cells::getInterior (const std::string& name) return &result->second; } +void MWWorld::Cells::rest () +{ + for (auto &interior : mInteriors) + { + interior.second.rest(); + } + + for (auto &exterior : mExteriors) + { + exterior.second.rest(); + } +} + MWWorld::CellStore *MWWorld::Cells::getCell (const ESM::CellId& id) { if (id.mPaged) diff --git a/apps/openmw/mwworld/cells.hpp b/apps/openmw/mwworld/cells.hpp index 0b7d9444f..bd730329b 100644 --- a/apps/openmw/mwworld/cells.hpp +++ b/apps/openmw/mwworld/cells.hpp @@ -61,6 +61,7 @@ namespace MWWorld /// @note name must be lower case Ptr getPtr (const std::string& name); + void rest (); /// Get all Ptrs referencing \a name in exterior cells /// @note Due to the current implementation of getPtr this only supports one Ptr per cell. diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 05ff1e326..5cd8346a7 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -17,6 +17,7 @@ #include #include "../mwbase/environment.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/world.hpp" #include "../mwmechanics/creaturestats.hpp" @@ -954,6 +955,29 @@ namespace MWWorld } } + void CellStore::rest() + { + if (mState == State_Loaded) + { + for (CellRefList::List::iterator it (mCreatures.mList.begin()); it!=mCreatures.mList.end(); ++it) + { + Ptr ptr = getCurrentPtr(&*it); + if (!ptr.isEmpty() && ptr.getRefData().getCount() > 0) + { + MWBase::Environment::get().getMechanicsManager()->restoreDynamicStats(ptr, true); + } + } + for (CellRefList::List::iterator it (mNpcs.mList.begin()); it!=mNpcs.mList.end(); ++it) + { + Ptr ptr = getCurrentPtr(&*it); + if (!ptr.isEmpty() && ptr.getRefData().getCount() > 0) + { + MWBase::Environment::get().getMechanicsManager()->restoreDynamicStats(ptr, true); + } + } + } + } + void CellStore::respawn() { if (mState == State_Loaded) diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index dd54bdd6a..bcf4b4ada 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -183,6 +183,8 @@ namespace MWWorld /// @return updated MWWorld::Ptr with the new CellStore pointer set. MWWorld::Ptr moveTo(const MWWorld::Ptr& object, MWWorld::CellStore* cellToMoveTo); + void rest(); + /// Make a copy of the given object and insert it into this cell. /// @note If you get a linker error here, this means the given type can not be inserted into a cell. /// The supported types are defined at the bottom of this file. diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 9b701d17b..fe311f8fc 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -3118,6 +3118,10 @@ namespace MWWorld return closestMarker; } + void World::rest() + { + mCells.rest(); + } void World::teleportToClosestMarker (const MWWorld::Ptr& ptr, const std::string& id) diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index e694d9d84..7df8d1af5 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -552,11 +552,9 @@ namespace MWWorld void enableActorCollision(const MWWorld::Ptr& actor, bool enable) override; RestPermitted canRest() const override; - ///< check if the player is allowed to rest \n - /// 0 - yes \n - /// 1 - only waiting \n - /// 2 - player is underwater \n - /// 3 - enemies are nearby (not implemented) + ///< check if the player is allowed to rest + + void rest() override; /// \todo Probably shouldn't be here MWRender::Animation* getAnimation(const MWWorld::Ptr &ptr) override; From ba2fd8b7959f13d1f3f548d98a8711b03744212f Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Wed, 26 Sep 2018 21:16:57 +0300 Subject: [PATCH 079/163] Rename reflected absorb spells setting and add it to Advanced tab --- apps/launcher/advancedpage.cpp | 2 ++ apps/openmw/mwmechanics/spellcasting.cpp | 2 +- .../reference/modding/settings/game.rst | 34 +++++++++---------- files/settings-default.cfg | 4 +-- files/ui/advancedpage.ui | 12 ++++++- 5 files changed, 33 insertions(+), 21 deletions(-) diff --git a/apps/launcher/advancedpage.cpp b/apps/launcher/advancedpage.cpp index 102463085..41f546af4 100644 --- a/apps/launcher/advancedpage.cpp +++ b/apps/launcher/advancedpage.cpp @@ -72,6 +72,7 @@ bool Launcher::AdvancedPage::loadSettings() loadSettingBool(canLootDuringDeathAnimationCheckBox, "can loot during death animation", "Game"); loadSettingBool(followersAttackOnSightCheckBox, "followers attack on sight", "Game"); loadSettingBool(preventMerchantEquippingCheckBox, "prevent merchant equipping", "Game"); + loadSettingBool(classicReflectedAbsorbSpellsCheckBox, "classic reflected absorb spells behavior", "Game"); loadSettingBool(rebalanceSoulGemValuesCheckBox, "rebalance soul gem values", "Game"); loadSettingBool(chargeForEveryFollowerCheckBox, "charge for every follower travelling", "Game"); loadSettingBool(enchantedWeaponsMagicalCheckBox, "enchanted weapons are magical", "Game"); @@ -131,6 +132,7 @@ void Launcher::AdvancedPage::saveSettings() saveSettingBool(followersAttackOnSightCheckBox, "followers attack on sight", "Game"); saveSettingBool(preventMerchantEquippingCheckBox, "prevent merchant equipping", "Game"); saveSettingBool(rebalanceSoulGemValuesCheckBox, "rebalance soul gem values", "Game"); + saveSettingBool(classicReflectedAbsorbSpellsCheckBox, "classic reflected absorb spells behavior", "Game"); saveSettingBool(chargeForEveryFollowerCheckBox, "charge for every follower travelling", "Game"); saveSettingBool(enchantedWeaponsMagicalCheckBox, "enchanted weapons are magical", "Game"); saveSettingBool(permanentBarterDispositionChangeCheckBox, "barter disposition change is permanent", "Game"); diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 14dfac283..f1ee2520b 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -582,7 +582,7 @@ namespace MWMechanics ActiveSpells::ActiveEffect effect_ = effect; effect_.mMagnitude *= -1; absorbEffects.push_back(effect_); - if (reflected && Settings::Manager::getBool("classic reflect absorb attribute behavior", "Game")) + if (reflected && Settings::Manager::getBool("classic reflected absorb spells behavior", "Game")) target.getClass().getCreatureStats(target).getActiveSpells().addSpell("", true, absorbEffects, mSourceName, caster.getClass().getCreatureStats(caster).getActorId()); else diff --git a/docs/source/reference/modding/settings/game.rst b/docs/source/reference/modding/settings/game.rst index baeac92b2..4c3f4579f 100644 --- a/docs/source/reference/modding/settings/game.rst +++ b/docs/source/reference/modding/settings/game.rst @@ -16,7 +16,7 @@ If the setting is 2, the crosshair is the color of the color crosshair owned set If the setting is 3, both the tool tip background and the crosshair are colored. The crosshair is not visible if crosshair is false. -This setting can only be configured by editing the settings configuration file. +This setting can be configured in Advanced tab of the launcher. show projectile damage ---------------------- @@ -27,7 +27,7 @@ show projectile damage If this setting is true, the damage bonus of arrows and bolts will show on their tooltip. -This setting can only be configured by editing the settings configuration file. +This setting can be toggled in Advanced tab of the launcher. show melee info --------------- @@ -38,7 +38,7 @@ show melee info If this setting is true, the reach and speed of weapons will show on their tooltip. -This setting can only be configured by editing the settings configuration file. +This setting can be toggled in Advanced tab of the launcher. show enchant chance ------------------- @@ -49,7 +49,7 @@ show enchant chance Whether or not the chance of success will be displayed in the enchanting menu. -This setting can only be configured by editing the settings configuration file. +This setting can be toggled in Advanced tab of the launcher. best attack ----------- @@ -73,13 +73,13 @@ can loot during death animation If this setting is true, the player is allowed to loot actors (e.g. summoned creatures) during death animation, if they are not in combat. However disposing corpses during death animation is not recommended - death counter may not be incremented, and this behaviour can break quests. -This is how original Morrowind behaves. +This is how Morrowind behaves. If this setting is false, player has to wait until end of death animation in all cases. This case is more safe, but makes using of summoned creatures exploit (looting summoned Dremoras and Golden Saints for expensive weapons) a lot harder. Conflicts with mannequin mods, which use SkipAnim to prevent end of death animation. -This setting can only be configured by editing the settings configuration file. +This setting can be toggled in Advanced tab of the launcher. difficulty ---------- @@ -97,19 +97,19 @@ and values above 500 will result in the player inflicting no damage. This setting can be controlled in game with the Difficulty slider in the Prefs panel of the Options menu. -classic reflect absorb attribute behavior +classic reflected absorb spells behavior ----------------------------------------- :Type: boolean :Range: True/False :Default: True -If this setting is true, "Absorb Attribute" spells which were reflected by the target are not "mirrored", -and the caster will absorb their own attribute resulting in no effect on both the caster and the target. +If this setting is true, effects of Absorb spells which were reflected by the target are not mirrored, +and the caster will absorb their own stat resulting in no effect on either the caster and the target. This makes the gameplay as a mage easier, but these spells become imbalanced. -This is how the original Morrowind behaves. +This is how Morrowind behaves. -This setting can only be configured by editing the settings configuration file. +This setting can be toggled in Advanced tab of the launcher. show effect duration -------------------- @@ -121,7 +121,7 @@ show effect duration Show the remaining duration of magic effects and lights if this setting is true. The remaining duration is displayed in the tooltip by hovering over the magical effect. -This setting can only be configured by editing the settings configuration file. +This setting can be toggled in Advanced tab of the launcher. enchanted weapons are magical ----------------------------- @@ -131,9 +131,9 @@ enchanted weapons are magical :Default: True Make enchanted weapons without Magical flag bypass normal weapons resistance (and weakness) certain creatures have. -This is how original Morrowind behaves. +This is how Morrowind behaves. -This setting can only be configured by editing the settings configuration file. +This setting can be toggled in Advanced tab of the launcher. prevent merchant equipping -------------------------- @@ -144,7 +144,7 @@ prevent merchant equipping Prevent merchants from equipping items that are sold to them. -This setting can only be configured by editing the settings configuration file. +This setting can be toggled in Advanced tab of the launcher. followers attack on sight ------------------------- @@ -157,7 +157,7 @@ Make player followers and escorters start combat with enemies who have started c Otherwise they wait for the enemies or the player to do an attack first. Please note this setting has not been extensively tested and could have side effects with certain quests. -This setting can only be configured by editing the settings configuration file. +This setting can be toggled in Advanced tab of the launcher. use additional anim sources --------------------------- @@ -182,4 +182,4 @@ barter disposition change is permanent If this setting is true, disposition change of merchants caused by trading will be permanent and won't be discarded upon exiting dialogue with them. This imitates the option Morrowind Code Patch offers. -This setting can be toggled with a checkbox in Advanced tab of the launcher. +This setting can be toggled in Advanced tab of the launcher. diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 3344e6bb4..ea6f6e7b6 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -200,8 +200,8 @@ best attack = false # Difficulty. Expressed as damage dealt and received. (e.g. -100 to 100). difficulty = 0 -# Replicate how reflected "absorb attribute" spells do not have any effect in Morrowind engine. The caster absorbs the attribute from themselves. -classic reflect absorb attribute behavior = true +# Make reflected Absorb spells have no practical effect, like in Morrowind. +classic reflected absorb spells behavior = true # Show duration of magic effect and lights in the spells window. show effect duration = false diff --git a/files/ui/advancedpage.ui b/files/ui/advancedpage.ui index 8395d028f..0793345c8 100644 --- a/files/ui/advancedpage.ui +++ b/files/ui/advancedpage.ui @@ -73,7 +73,7 @@ Enchanted weapons are magical - + @@ -86,6 +86,16 @@ + + + + <html><head/><body><p>Effects of reflected Absorb spells are not mirrored -- like in Morrowind.</p></body></html> + + + Classic reflected Absorb spells behavior + + + From ee4ca87352fd8731e9cb96cf42e5f82beec527ba Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 26 Sep 2018 23:13:18 +0400 Subject: [PATCH 080/163] Traverse child nodes of cell node in the editor (bug #4654) --- CHANGELOG.md | 1 + apps/opencs/view/render/cell.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6283d3f29..d5c7364b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -128,6 +128,7 @@ Bug #4644: %Name should be available for all actors, not just for NPCs Bug #4648: Hud thinks that throwing weapons have condition Bug #4649: Levelup fully restores health + Bug #4654: Editor: UpdateVisitor does not initialize skeletons for animated objects Feature #912: Editor: Add missing icons to UniversalId tables Feature #1617: Editor: Enchantment effect record verifier Feature #1645: Casting effects from objects diff --git a/apps/opencs/view/render/cell.cpp b/apps/opencs/view/render/cell.cpp index 552a54ac2..a0c408df0 100644 --- a/apps/opencs/view/render/cell.cpp +++ b/apps/opencs/view/render/cell.cpp @@ -47,6 +47,7 @@ namespace CSVRender virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) { + traverse(node, nv); CellNodeContainer* container = static_cast(node->getUserData()); container->getCell()->updateLand(); } From 8444ee9981226e6f739b4df8ced94ef7bd9f7abe Mon Sep 17 00:00:00 2001 From: Kyle Cooley Date: Fri, 13 Jul 2018 21:48:59 -0500 Subject: [PATCH 081/163] Start rendering npc's --- apps/opencs/CMakeLists.txt | 2 +- apps/opencs/view/render/actor.cpp | 196 +++++++++++++++++++++++++++++ apps/opencs/view/render/actor.hpp | 51 ++++++++ apps/opencs/view/render/object.cpp | 25 +++- apps/openmw/mwrender/animation.cpp | 34 +---- components/CMakeLists.txt | 2 +- components/esm/mappings.cpp | 134 ++++++++++++++++++++ components/esm/mappings.hpp | 16 +++ components/sceneutil/visitor.cpp | 19 ++- components/sceneutil/visitor.hpp | 30 +++++ 10 files changed, 471 insertions(+), 38 deletions(-) create mode 100644 apps/opencs/view/render/actor.cpp create mode 100644 apps/opencs/view/render/actor.hpp create mode 100644 components/esm/mappings.cpp create mode 100644 components/esm/mappings.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index f2821f184..26713f925 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -93,7 +93,7 @@ opencs_units (view/render opencs_units_noqt (view/render lighting lightingday lightingnight lightingbright object cell terrainstorage tagbase - cellarrow cellmarker cellborder pathgrid + cellarrow cellmarker cellborder pathgrid actor ) opencs_hdrs_noqt (view/render diff --git a/apps/opencs/view/render/actor.cpp b/apps/opencs/view/render/actor.cpp new file mode 100644 index 000000000..1273b94c6 --- /dev/null +++ b/apps/opencs/view/render/actor.cpp @@ -0,0 +1,196 @@ +#include "actor.hpp" + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "../../model/world/data.hpp" + +namespace CSVRender +{ + Actor::Actor(const std::string& id, int type, CSMWorld::Data& data) + : mId(id) + , mType(type) + , mData(data) + , mSkeleton(nullptr) + , mBaseNode(new osg::Group()) + { + } + + osg::Group* Actor::getBaseNode() + { + return mBaseNode; + } + + void Actor::update() + { + const std::string MeshPrefix = "meshes\\"; + const unsigned int FemaleFlag = ESM::BodyPart::BPF_Female; + + auto& bodyParts = mData.getBodyParts(); + auto& races = mData.getRaces(); + auto& referenceables = mData.getReferenceables(); + auto sceneMgr = mData.getResourceSystem()->getSceneManager(); + + + // Remove children + mBaseNode->removeChildren(0, mBaseNode->getNumChildren()); + + // Npcs and creatures are handled differently + if (mType == CSMWorld::UniversalId::Type_Npc) + { + auto& npc = dynamic_cast& >(referenceables.getRecord(mId)).get(); + + auto isBeast = [&](std::string race) -> bool { + int index = races.searchId(race); + if (index != -1 && !races.getRecord(index).isDeleted()) + return races.getRecord(index).get().mData.mFlags & ESM::Race::Beast; + else + return false; + }; + + // Load skeleton + std::string skeletonResource; + if (isBeast(npc.mRace)) { + std::cout << "is beast\n"; + skeletonResource = "base_animkna.nif"; + } + else if (npc.isMale()) { + std::cout << "is male\n"; + skeletonResource = "base_anim.nif"; + } + else { + std::cout << "is female\n"; + skeletonResource = "base_anim_female.nif"; + } + + std::string skeletonModel = MeshPrefix + skeletonResource; + skeletonModel = Misc::ResourceHelpers::correctActorModelPath(skeletonModel, mData.getResourceSystem()->getVFS()); + { + osg::ref_ptr temp = sceneMgr->getInstance(skeletonModel); + mSkeleton = dynamic_cast(temp.get()); + if (!mSkeleton) + { + mSkeleton = new SceneUtil::Skeleton(); + mSkeleton->addChild(temp); + } + mBaseNode->addChild(mSkeleton); + } + + // Map bone names to bones + SceneUtil::NodeMapVisitor::NodeMap nodeMap; + SceneUtil::NodeMapVisitor nmVisitor(nodeMap); + mSkeleton->accept(nmVisitor); + + if (!npc.isMale()) { + for (auto it : nodeMap) { + std::cout << it.first << "\n"; + } + } + + + // Female mesh has some drawables attached, get rid of them + SceneUtil::HideDrawablesVisitor hdVisitor; + mSkeleton->accept(hdVisitor); + + // Convenience method to retrieve the mesh name of a body part + auto getBodyPartMesh = [&](std::string bpName) -> std::string { + int index = bodyParts.searchId(bpName); + if (index != -1 && !bodyParts.getRecord(index).isDeleted()) + return MeshPrefix + bodyParts.getRecord(index).get().mModel; + else + return ""; + }; + + using BPRaceKey = std::tuple; + using RaceToBPMap = std::map; + // Convenience method to generate a map from body part + race to mesh name + auto genRaceToBodyPartMap = [&](RaceToBPMap& bpMap) { + int size = bodyParts.getSize(); + for (int i = 0; i < size; ++i) + { + auto& record = bodyParts.getRecord(i); + if (!record.isDeleted()) + { + auto& bodyPart = record.get(); + bpMap.emplace( + BPRaceKey(bodyPart.mData.mPart, bodyPart.mData.mFlags & FemaleFlag ? 1 : 0, bodyPart.mRace), + MeshPrefix + bodyPart.mModel); + } + } + }; + + // Generate mapping + RaceToBPMap r2bpMap; + genRaceToBodyPartMap(r2bpMap); + + // Convenience method to add a body part + auto addBodyPart = [&](ESM::PartReferenceType type, std::string mesh) { + // Retrieve mesh name if necessary + if (mesh.empty()) + { + auto meshResult = r2bpMap.find(BPRaceKey(ESM::getMeshPart(type), npc.isMale() ? 0 : 1, npc.mRace)); + if (meshResult != r2bpMap.end()) + { + mesh = meshResult->second; + } + } + + // Attach to skeleton + std::string boneName = ESM::getBoneName(type); + auto node = nodeMap.find(boneName); + if (!mesh.empty() && node != nodeMap.end()) + { + auto instance = sceneMgr->getInstance(mesh); + if (!npc.isMale() && type == ESM::PRT_LHand) { + SceneUtil::NodeMapVisitor::NodeMap handNodeMap; + SceneUtil::NodeMapVisitor nmVisitor(handNodeMap); + instance->accept(nmVisitor); + + std::cout << "Left hand\n"; + for (auto it : handNodeMap) { + std::cout << it.first << std::endl; + } + } + SceneUtil::attach(instance, mSkeleton, boneName, node->second); + } + }; + + // Add body parts + for (unsigned int i = 0; i < ESM::PRT_Count; ++i) + { + auto part = static_cast(i); + switch (part) + { + case ESM::PRT_Head: + addBodyPart(part, getBodyPartMesh(npc.mHead)); + break; + case ESM::PRT_Hair: + addBodyPart(part, getBodyPartMesh(npc.mHair)); + break; + case ESM::PRT_Skirt: + case ESM::PRT_Shield: + case ESM::PRT_RPauldron: + case ESM::PRT_LPauldron: + case ESM::PRT_Weapon: + // No body part mesh associated + break; + default: + addBodyPart(part, ""); + } + } + // Post setup + mSkeleton->markDirty(); + mSkeleton->setActive(SceneUtil::Skeleton::Active); + } + } +} diff --git a/apps/opencs/view/render/actor.hpp b/apps/opencs/view/render/actor.hpp new file mode 100644 index 000000000..10f4de558 --- /dev/null +++ b/apps/opencs/view/render/actor.hpp @@ -0,0 +1,51 @@ +#ifndef OPENCS_VIEW_RENDER_ACTOR_H +#define OPENCS_VIEW_RENDER_ACTOR_H + +#include + +#include + +namespace osg +{ + class Group; +} + +namespace CSMWorld +{ + class Data; +} + +namespace SceneUtil +{ + class Skeleton; +} + +namespace CSVRender +{ + /// Handles loading an npc or creature + class Actor + { + public: + /// Creates an actor. + /// \param id The referenceable id + /// \param type The record type + /// \param data The data store + Actor(const std::string& id, int type, CSMWorld::Data& data); + + /// Retrieves the base node that meshes are attached to + osg::Group* getBaseNode(); + + /// (Re)creates the npc or creature renderable + void update(); + + private: + std::string mId; + int mType; + CSMWorld::Data& mData; + + SceneUtil::Skeleton* mSkeleton; + osg::ref_ptr mBaseNode; + }; +} + +#endif diff --git a/apps/opencs/view/render/object.cpp b/apps/opencs/view/render/object.cpp index 2b1e3adde..b8e171650 100644 --- a/apps/opencs/view/render/object.cpp +++ b/apps/opencs/view/render/object.cpp @@ -1,6 +1,8 @@ #include "object.hpp" #include +#include +#include #include #include @@ -11,6 +13,7 @@ #include #include #include +#include #include @@ -28,7 +31,9 @@ #include #include #include +#include +#include "actor.hpp" #include "mask.hpp" @@ -84,6 +89,7 @@ void CSVRender::Object::update() const CSMWorld::RefIdCollection& referenceables = mData.getReferenceables(); int index = referenceables.searchId (mReferenceableId); + int recordType = -1; const ESM::Light* light = NULL; if (index==-1) @@ -96,7 +102,7 @@ void CSVRender::Object::update() referenceables.findColumnIndex (CSMWorld::Columns::ColumnId_Model)). toString().toUtf8().constData(); - int recordType = + recordType = referenceables.getData (index, referenceables.findColumnIndex(CSMWorld::Columns::ColumnId_RecordType)).toInt(); if (recordType == CSMWorld::UniversalId::Type_Light) @@ -112,7 +118,7 @@ void CSVRender::Object::update() model = "marker_creature.nif"; } - if (model.empty()) + if (recordType != CSMWorld::UniversalId::Type_Npc && model.empty()) error = 2; } @@ -126,9 +132,18 @@ void CSVRender::Object::update() { try { - std::string path = "meshes\\" + model; - - mResourceSystem->getSceneManager()->getInstance(path, mBaseNode); + if (recordType == CSMWorld::UniversalId::Type_Npc) + { + std::cout << "recordType: Npc\n"; + Actor actor(mReferenceableId, recordType, mData); + actor.update(); + mBaseNode->addChild(actor.getBaseNode()); + } + else + { + std::string path = "meshes\\" + model; + mResourceSystem->getSceneManager()->getInstance(path, mBaseNode); + } } catch (std::exception& e) { diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 7410dbf03..e4a7c94b3 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -91,32 +91,6 @@ namespace std::vector > mToRemove; }; - class NodeMapVisitor : public osg::NodeVisitor - { - public: - typedef std::map > NodeMap; - - NodeMapVisitor(NodeMap& map) - : osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) - , mMap(map) - {} - - void apply(osg::MatrixTransform& trans) - { - // Take transformation for first found node in file - const std::string nodeName = Misc::StringUtils::lowerCase(trans.getName()); - if (mMap.find(nodeName) == mMap.end()) - { - mMap[nodeName] = &trans; - } - - traverse(trans); - } - - private: - NodeMap& mMap; - }; - NifOsg::TextKeyMap::const_iterator findGroupStart(const NifOsg::TextKeyMap &keys, const std::string &groupname) { NifOsg::TextKeyMap::const_iterator iter(keys.begin()); @@ -468,7 +442,7 @@ namespace MWRender } if (mDone) return; - + // Set the starting time to measure glow duration from if this is a temporary glow if ((mDuration >= 0) && mStartingTime == 0) mStartingTime = nv->getFrameStamp()->getSimulationTime(); @@ -1042,7 +1016,7 @@ namespace MWRender { if (!mNodeMapCreated && mObjectRoot) { - NodeMapVisitor visitor(mNodeMap); + SceneUtil::NodeMapVisitor visitor(mNodeMap); mObjectRoot->accept(visitor); mNodeMapCreated = true; } @@ -1313,7 +1287,7 @@ namespace MWRender if(state.getTime() >= state.mLoopStopTime) break; - } + } if(timepassed <= 0.0f) break; @@ -1528,7 +1502,7 @@ namespace MWRender osg::ref_ptr glowUpdater = new GlowUpdater(texUnit, glowColor, textures, node, glowDuration, mResourceSystem); mGlowUpdater = glowUpdater; node->addUpdateCallback(glowUpdater); - + // set a texture now so that the ShaderVisitor can find it osg::ref_ptr writableStateSet = NULL; if (!node->getStateSet()) diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 2da7c80bf..e7e0ea0da 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -77,7 +77,7 @@ add_component_dir (esm loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter savedgame journalentry queststate locals globalscript player objectstate cellid cellstate globalmap inventorystate containerstate npcstate creaturestate dialoguestate statstate npcstats creaturestats weatherstate quickkeys fogstate spellstate activespells creaturelevliststate doorstate projectilestate debugprofile - aisequence magiceffects util custommarkerstate stolenitems transport animationstate controlsstate + aisequence magiceffects util custommarkerstate stolenitems transport animationstate controlsstate mappings ) add_component_dir (esmterrain diff --git a/components/esm/mappings.cpp b/components/esm/mappings.cpp new file mode 100644 index 000000000..440e73573 --- /dev/null +++ b/components/esm/mappings.cpp @@ -0,0 +1,134 @@ +#include "mappings.hpp" + +#include + +namespace ESM +{ + ESM::BodyPart::MeshPart getMeshPart(ESM::PartReferenceType type) + { + switch(type) + { + case ESM::PRT_Head: + return ESM::BodyPart::MP_Head; + case ESM::PRT_Hair: + return ESM::BodyPart::MP_Hair; + case ESM::PRT_Neck: + return ESM::BodyPart::MP_Neck; + case ESM::PRT_Cuirass: + return ESM::BodyPart::MP_Chest; + case ESM::PRT_Groin: + return ESM::BodyPart::MP_Groin; + case ESM::PRT_RHand: + return ESM::BodyPart::MP_Hand; + case ESM::PRT_LHand: + return ESM::BodyPart::MP_Hand; + case ESM::PRT_RWrist: + return ESM::BodyPart::MP_Wrist; + case ESM::PRT_LWrist: + return ESM::BodyPart::MP_Wrist; + case ESM::PRT_RForearm: + return ESM::BodyPart::MP_Forearm; + case ESM::PRT_LForearm: + return ESM::BodyPart::MP_Forearm; + case ESM::PRT_RUpperarm: + return ESM::BodyPart::MP_Upperarm; + case ESM::PRT_LUpperarm: + return ESM::BodyPart::MP_Upperarm; + case ESM::PRT_RFoot: + return ESM::BodyPart::MP_Foot; + case ESM::PRT_LFoot: + return ESM::BodyPart::MP_Foot; + case ESM::PRT_RAnkle: + return ESM::BodyPart::MP_Ankle; + case ESM::PRT_LAnkle: + return ESM::BodyPart::MP_Ankle; + case ESM::PRT_RKnee: + return ESM::BodyPart::MP_Knee; + case ESM::PRT_LKnee: + return ESM::BodyPart::MP_Knee; + case ESM::PRT_RLeg: + return ESM::BodyPart::MP_Upperleg; + case ESM::PRT_LLeg: + return ESM::BodyPart::MP_Upperleg; + case ESM::PRT_Tail: + return ESM::BodyPart::MP_Tail; + default: + throw std::runtime_error("PartReferenceType " + + std::to_string(type) + " not associated with a mesh part"); + } + } + + std::string getBoneName(ESM::PartReferenceType type) + { + switch(type) + { + case ESM::PRT_Head: + return "head"; + case ESM::PRT_Hair: + return "head"; // This is purposeful. + case ESM::PRT_Neck: + return "neck"; + case ESM::PRT_Cuirass: + return "chest"; + case ESM::PRT_Groin: + return "groin"; + case ESM::PRT_Skirt: + return "groin"; + case ESM::PRT_RHand: + return "right hand"; + case ESM::PRT_LHand: + return "left hand"; + case ESM::PRT_RWrist: + return "right wrist"; + case ESM::PRT_LWrist: + return "left wrist"; + case ESM::PRT_Shield: + return "shield bone"; + case ESM::PRT_RForearm: + return "right forearm"; + case ESM::PRT_LForearm: + return "left forearm"; + case ESM::PRT_RUpperarm: + return "right upper arm"; + case ESM::PRT_LUpperarm: + return "left upper arm"; + case ESM::PRT_RFoot: + return "right foot"; + case ESM::PRT_LFoot: + return "left foot"; + case ESM::PRT_RAnkle: + return "right ankle"; + case ESM::PRT_LAnkle: + return "left ankle"; + case ESM::PRT_RKnee: + return "right knee"; + case ESM::PRT_LKnee: + return "left knee"; + case ESM::PRT_RLeg: + return "right upper leg"; + case ESM::PRT_LLeg: + return "left upper leg"; + case ESM::PRT_RPauldron: + return "right clavicle"; + case ESM::PRT_LPauldron: + return "left clavicle"; + case ESM::PRT_Weapon: + return "weapon bone"; + case ESM::PRT_Tail: + return "tail"; + default: + throw std::runtime_error("unknown PartReferenceType"); + } + } + + std::string getMeshFilter(ESM::PartReferenceType type) + { + switch(type) + { + case ESM::PRT_Hair: + return "hair"; + default: + return getBoneName(type); + } + } +} diff --git a/components/esm/mappings.hpp b/components/esm/mappings.hpp new file mode 100644 index 000000000..f930fef15 --- /dev/null +++ b/components/esm/mappings.hpp @@ -0,0 +1,16 @@ +#ifndef OPENMW_ESM_MAPPINGS_H +#define OPENMW_ESM_MAPPINGS_H + +#include + +#include +#include + +namespace ESM +{ + ESM::BodyPart::MeshPart getMeshPart(ESM::PartReferenceType type); + std::string getBoneName(ESM::PartReferenceType type); + std::string getMeshFilter(ESM::PartReferenceType type); +} + +#endif diff --git a/components/sceneutil/visitor.cpp b/components/sceneutil/visitor.cpp index 2f6123e34..5aaeb459e 100644 --- a/components/sceneutil/visitor.cpp +++ b/components/sceneutil/visitor.cpp @@ -1,5 +1,6 @@ #include "visitor.hpp" +#include #include #include @@ -23,7 +24,7 @@ namespace SceneUtil { if (Misc::StringUtils::ciEqual(node.className(), mNameToFind)) mFoundNodes.push_back(&node); - + traverse(node); } @@ -54,4 +55,20 @@ namespace SceneUtil partsys->setFreezeOnCull(false); } + void NodeMapVisitor::apply(osg::MatrixTransform& trans) + { + // Take transformation for first found node in file + const std::string nodeName = Misc::StringUtils::lowerCase(trans.getName()); + if (mMap.find(nodeName) == mMap.end()) + { + mMap[nodeName] = &trans; + } + + traverse(trans); + } + + void HideDrawablesVisitor::apply(osg::Drawable& drawable) + { + drawable.setNodeMask(0); + } } diff --git a/components/sceneutil/visitor.hpp b/components/sceneutil/visitor.hpp index 265fd6d02..bd3945296 100644 --- a/components/sceneutil/visitor.hpp +++ b/components/sceneutil/visitor.hpp @@ -1,6 +1,7 @@ #ifndef OPENMW_COMPONENTS_SCENEUTIL_VISITOR_H #define OPENMW_COMPONENTS_SCENEUTIL_VISITOR_H +#include #include // Commonly used scene graph visitors @@ -58,6 +59,35 @@ namespace SceneUtil virtual void apply(osg::Drawable& drw); }; + /// Maps names to nodes + class NodeMapVisitor : public osg::NodeVisitor + { + public: + typedef std::map > NodeMap; + + NodeMapVisitor(NodeMap& map) + : osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) + , mMap(map) + {} + + void apply(osg::MatrixTransform& trans); + + private: + NodeMap& mMap; + }; + + /// Hides all attached drawables + class HideDrawablesVisitor : public osg::NodeVisitor + { + public: + HideDrawablesVisitor() + : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) + { + } + + void apply(osg::Drawable& drawable) override; + }; + } #endif From e2ac392a4076c09aeb42d4a1f9213c83cb6d46ad Mon Sep 17 00:00:00 2001 From: Kyle Cooley Date: Tue, 17 Jul 2018 21:28:05 -0500 Subject: [PATCH 082/163] Move common stuff to scene util, fix errors with 1st person meshes --- apps/opencs/view/render/actor.cpp | 247 ++++++++++++-------------- apps/openmw/mwrender/animation.cpp | 4 +- apps/openmw/mwrender/npcanimation.cpp | 33 +--- components/CMakeLists.txt | 1 + components/sceneutil/actorutil.cpp | 30 ++++ components/sceneutil/actorutil.hpp | 11 ++ components/sceneutil/visitor.cpp | 89 +++++++++- components/sceneutil/visitor.hpp | 40 ++++- 8 files changed, 290 insertions(+), 165 deletions(-) create mode 100644 components/sceneutil/actorutil.cpp create mode 100644 components/sceneutil/actorutil.hpp diff --git a/apps/opencs/view/render/actor.cpp b/apps/opencs/view/render/actor.cpp index 1273b94c6..93fff270a 100644 --- a/apps/opencs/view/render/actor.cpp +++ b/apps/opencs/view/render/actor.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -45,152 +46,140 @@ namespace CSVRender // Remove children mBaseNode->removeChildren(0, mBaseNode->getNumChildren()); - // Npcs and creatures are handled differently - if (mType == CSMWorld::UniversalId::Type_Npc) + try { - auto& npc = dynamic_cast& >(referenceables.getRecord(mId)).get(); - - auto isBeast = [&](std::string race) -> bool { - int index = races.searchId(race); - if (index != -1 && !races.getRecord(index).isDeleted()) - return races.getRecord(index).get().mData.mFlags & ESM::Race::Beast; - else - return false; - }; - - // Load skeleton - std::string skeletonResource; - if (isBeast(npc.mRace)) { - std::cout << "is beast\n"; - skeletonResource = "base_animkna.nif"; - } - else if (npc.isMale()) { - std::cout << "is male\n"; - skeletonResource = "base_anim.nif"; - } - else { - std::cout << "is female\n"; - skeletonResource = "base_anim_female.nif"; - } - - std::string skeletonModel = MeshPrefix + skeletonResource; - skeletonModel = Misc::ResourceHelpers::correctActorModelPath(skeletonModel, mData.getResourceSystem()->getVFS()); + // Npcs and creatures are handled differently + if (mType == CSMWorld::UniversalId::Type_Npc) { - osg::ref_ptr temp = sceneMgr->getInstance(skeletonModel); - mSkeleton = dynamic_cast(temp.get()); - if (!mSkeleton) + auto& npc = dynamic_cast& >(referenceables.getRecord(mId)).get(); + auto& race = dynamic_cast& >(races.getRecord(npc.mRace)).get(); + + bool is1stPerson = false; + bool isFemale = !npc.isMale(); + bool isBeast = race.mData.mFlags & ESM::Race::Beast; + bool isWerewolf = false; + + // Load skeleton + std::string skeletonModel = SceneUtil::getActorSkeleton(is1stPerson, isFemale, isBeast, isWerewolf); + skeletonModel = Misc::ResourceHelpers::correctActorModelPath(skeletonModel, mData.getResourceSystem()->getVFS()); { - mSkeleton = new SceneUtil::Skeleton(); - mSkeleton->addChild(temp); - } - mBaseNode->addChild(mSkeleton); - } - - // Map bone names to bones - SceneUtil::NodeMapVisitor::NodeMap nodeMap; - SceneUtil::NodeMapVisitor nmVisitor(nodeMap); - mSkeleton->accept(nmVisitor); - - if (!npc.isMale()) { - for (auto it : nodeMap) { - std::cout << it.first << "\n"; - } - } - - - // Female mesh has some drawables attached, get rid of them - SceneUtil::HideDrawablesVisitor hdVisitor; - mSkeleton->accept(hdVisitor); - - // Convenience method to retrieve the mesh name of a body part - auto getBodyPartMesh = [&](std::string bpName) -> std::string { - int index = bodyParts.searchId(bpName); - if (index != -1 && !bodyParts.getRecord(index).isDeleted()) - return MeshPrefix + bodyParts.getRecord(index).get().mModel; - else - return ""; - }; - - using BPRaceKey = std::tuple; - using RaceToBPMap = std::map; - // Convenience method to generate a map from body part + race to mesh name - auto genRaceToBodyPartMap = [&](RaceToBPMap& bpMap) { - int size = bodyParts.getSize(); - for (int i = 0; i < size; ++i) - { - auto& record = bodyParts.getRecord(i); - if (!record.isDeleted()) + osg::ref_ptr temp = sceneMgr->getInstance(skeletonModel); + mSkeleton = dynamic_cast(temp.get()); + if (!mSkeleton) { - auto& bodyPart = record.get(); - bpMap.emplace( - BPRaceKey(bodyPart.mData.mPart, bodyPart.mData.mFlags & FemaleFlag ? 1 : 0, bodyPart.mRace), - MeshPrefix + bodyPart.mModel); + mSkeleton = new SceneUtil::Skeleton(); + mSkeleton->addChild(temp); } + mBaseNode->addChild(mSkeleton); } - }; - // Generate mapping - RaceToBPMap r2bpMap; - genRaceToBodyPartMap(r2bpMap); + // Map bone names to bones + SceneUtil::NodeMapVisitor::NodeMap nodeMap; + SceneUtil::NodeMapVisitor nmVisitor(nodeMap); + mSkeleton->accept(nmVisitor); - // Convenience method to add a body part - auto addBodyPart = [&](ESM::PartReferenceType type, std::string mesh) { - // Retrieve mesh name if necessary - if (mesh.empty()) - { - auto meshResult = r2bpMap.find(BPRaceKey(ESM::getMeshPart(type), npc.isMale() ? 0 : 1, npc.mRace)); - if (meshResult != r2bpMap.end()) + // Female mesh has some drawables attached, get rid of them + SceneUtil::CleanObjectRootVisitor cleanVisitor; + mSkeleton->accept(cleanVisitor); + cleanVisitor.remove(); + + // Convenience method to retrieve the mesh name of a body part + auto getBodyPartMesh = [&](std::string bpName) -> std::string { + int index = bodyParts.searchId(bpName); + if (index != -1 && !bodyParts.getRecord(index).isDeleted()) + return MeshPrefix + bodyParts.getRecord(index).get().mModel; + else + return ""; + }; + + using BPRaceKey = std::tuple; + using RaceToBPMap = std::map; + // Convenience method to generate a map from body part + race to mesh name + auto genRaceToBodyPartMap = [&](RaceToBPMap& bpMap) { + int size = bodyParts.getSize(); + for (int i = 0; i < size; ++i) { - mesh = meshResult->second; - } - } + auto& record = bodyParts.getRecord(i); + if (!record.isDeleted()) + { + // Method to check if 1st person part or not + auto is1stPersonPart = [](std::string name) { + return name.size() >= 4 && name.find(".1st", name.size() - 4) != std::string::npos; + }; - // Attach to skeleton - std::string boneName = ESM::getBoneName(type); - auto node = nodeMap.find(boneName); - if (!mesh.empty() && node != nodeMap.end()) - { - auto instance = sceneMgr->getInstance(mesh); - if (!npc.isMale() && type == ESM::PRT_LHand) { - SceneUtil::NodeMapVisitor::NodeMap handNodeMap; - SceneUtil::NodeMapVisitor nmVisitor(handNodeMap); - instance->accept(nmVisitor); + auto& bodyPart = record.get(); + if (bodyPart.mData.mType != ESM::BodyPart::MT_Skin || is1stPersonPart(bodyPart.mId)) + continue; - std::cout << "Left hand\n"; - for (auto it : handNodeMap) { - std::cout << it.first << std::endl; + bpMap.emplace( + BPRaceKey(bodyPart.mData.mPart, bodyPart.mData.mFlags & FemaleFlag ? 1 : 0, bodyPart.mRace), + MeshPrefix + bodyPart.mModel); } } - SceneUtil::attach(instance, mSkeleton, boneName, node->second); - } - }; + }; - // Add body parts - for (unsigned int i = 0; i < ESM::PRT_Count; ++i) - { - auto part = static_cast(i); - switch (part) + // Generate mapping + RaceToBPMap r2bpMap; + genRaceToBodyPartMap(r2bpMap); + + // Convenience method to add a body part + auto addBodyPart = [&](ESM::PartReferenceType type, std::string mesh) { + // Retrieve mesh name if necessary + if (mesh.empty()) + { + auto meshResult = r2bpMap.find(BPRaceKey(ESM::getMeshPart(type), isFemale ? 1 : 0, npc.mRace)); + if (meshResult != r2bpMap.end()) + { + mesh = meshResult->second; + } + else if (isFemale){ + meshResult = r2bpMap.find(BPRaceKey(ESM::getMeshPart(type), 0, npc.mRace)); + if (meshResult != r2bpMap.end()) + mesh = meshResult->second; + } + } + + // Attach to skeleton + std::string boneName = ESM::getBoneName(type); + auto node = nodeMap.find(boneName); + if (!mesh.empty() && node != nodeMap.end()) + { + auto instance = sceneMgr->getInstance(mesh); + SceneUtil::attach(instance, mSkeleton, boneName, node->second); + } + }; + + // Add body parts + for (unsigned int i = 0; i < ESM::PRT_Count; ++i) { - case ESM::PRT_Head: - addBodyPart(part, getBodyPartMesh(npc.mHead)); - break; - case ESM::PRT_Hair: - addBodyPart(part, getBodyPartMesh(npc.mHair)); - break; - case ESM::PRT_Skirt: - case ESM::PRT_Shield: - case ESM::PRT_RPauldron: - case ESM::PRT_LPauldron: - case ESM::PRT_Weapon: - // No body part mesh associated - break; - default: - addBodyPart(part, ""); + auto part = static_cast(i); + switch (part) + { + case ESM::PRT_Head: + addBodyPart(part, getBodyPartMesh(npc.mHead)); + break; + case ESM::PRT_Hair: + addBodyPart(part, getBodyPartMesh(npc.mHair)); + break; + case ESM::PRT_Skirt: + case ESM::PRT_Shield: + case ESM::PRT_RPauldron: + case ESM::PRT_LPauldron: + case ESM::PRT_Weapon: + // No body part mesh associated + break; + default: + addBodyPart(part, ""); + } } + // Post setup + mSkeleton->markDirty(); + mSkeleton->setActive(SceneUtil::Skeleton::Active); } - // Post setup - mSkeleton->markDirty(); - mSkeleton->setActive(SceneUtil::Skeleton::Active); + } + catch (std::exception& e) + { + std::cout << "Caught exception: " << e.what() << std::endl; } } } diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index e4a7c94b3..484f90ab5 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -1339,7 +1339,7 @@ namespace MWRender { osg::ref_ptr created = sceneMgr->getInstance(model); - CleanObjectRootVisitor removeDrawableVisitor; + SceneUtil::CleanObjectRootVisitor removeDrawableVisitor; created->accept(removeDrawableVisitor); removeDrawableVisitor.remove(); @@ -1408,7 +1408,7 @@ namespace MWRender if (isCreature) { - RemoveTriBipVisitor removeTriBipVisitor; + SceneUtil::RemoveTriBipVisitor removeTriBipVisitor; mObjectRoot->accept(removeTriBipVisitor); removeTriBipVisitor.remove(); } diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 98f8ce892..2716ea19b 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -304,7 +305,7 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, osg::ref_ptr par void NpcAnimation::setViewMode(NpcAnimation::ViewMode viewMode) { assert(viewMode != VM_HeadOnly); - if(mViewMode == viewMode) + if(mViewMode == viewMode) return; mViewMode = viewMode; @@ -451,37 +452,15 @@ void NpcAnimation::updateNpcBase() } } + bool is1stPerson = mViewMode == VM_FirstPerson; bool isBeast = (race->mData.mFlags & ESM::Race::Beast) != 0; - std::string smodel; - if (mViewMode != VM_FirstPerson) - { - if (isWerewolf) - smodel = "meshes\\wolf\\skin.nif"; - else if (isBeast) - smodel = "meshes\\base_animkna.nif"; - else if (isFemale) - smodel = "meshes\\base_anim_female.nif"; - else - smodel = "meshes\\base_anim.nif"; - } - else - { - if (isWerewolf) - smodel = "meshes\\wolf\\skin.1st.nif"; - else if (isBeast) - smodel = "meshes\\base_animkna.1st.nif"; - else if (isFemale) - smodel = "meshes\\base_anim_female.1st.nif"; - else - smodel = "meshes\\base_anim.1st.nif"; - } - + std::string smodel = SceneUtil::getActorSkeleton(is1stPerson, isFemale, isBeast, isWerewolf); smodel = Misc::ResourceHelpers::correctActorModelPath(smodel, mResourceSystem->getVFS()); setObjectRoot(smodel, true, true, false); - if(mViewMode != VM_FirstPerson) + if(!is1stPerson) { const std::string base = "meshes\\xbase_anim.nif"; if (smodel != base) @@ -675,7 +654,7 @@ PartHolderPtr NpcAnimation::insertBoundedPart(const std::string& model, const st } osg::Vec3f NpcAnimation::runAnimation(float timepassed) -{ +{ osg::Vec3f ret = Animation::runAnimation(timepassed); mHeadAnimationTime->update(timepassed); diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index e7e0ea0da..7af76137c 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -51,6 +51,7 @@ add_component_dir (shader add_component_dir (sceneutil clone attach visitor util statesetupdater controller skeleton riggeometry morphgeometry lightcontroller lightmanager lightutil positionattitudetransform workqueue unrefqueue pathgridutil waterutil writescene serialize optimizer + actorutil ) add_component_dir (nif diff --git a/components/sceneutil/actorutil.cpp b/components/sceneutil/actorutil.cpp new file mode 100644 index 000000000..988a61f60 --- /dev/null +++ b/components/sceneutil/actorutil.cpp @@ -0,0 +1,30 @@ +#include "actorutil.hpp" + +namespace SceneUtil +{ + std::string getActorSkeleton(bool firstPerson, bool isFemale, bool isBeast, bool isWerewolf) + { + if (!firstPerson) + { + if (isWerewolf) + return "meshes\\wolf\\skin.nif"; + else if (isBeast) + return "meshes\\base_animkna.nif"; + else if (isFemale) + return "meshes\\base_anim_female.nif"; + else + return "meshes\\base_anim.nif"; + } + else + { + if (isWerewolf) + return "meshes\\wolf\\skin.1st.nif"; + else if (isBeast) + return "meshes\\base_animkna.1st.nif"; + else if (isFemale) + return "meshes\\base_anim_female.1st.nif"; + else + return "meshes\\base_anim.1st.nif"; + } + } +} diff --git a/components/sceneutil/actorutil.hpp b/components/sceneutil/actorutil.hpp new file mode 100644 index 000000000..7bdbbaa92 --- /dev/null +++ b/components/sceneutil/actorutil.hpp @@ -0,0 +1,11 @@ +#ifndef OPENMW_COMPONENTS_SCENEUTIL_ACTORUTIL_HPP +#define OPENMW_COMPONENTS_SCENEUTIL_ACTORUTIL_HPP + +#include + +namespace SceneUtil +{ + std::string getActorSkeleton(bool firstPerson, bool female, bool beast, bool werewolf); +} + +#endif diff --git a/components/sceneutil/visitor.cpp b/components/sceneutil/visitor.cpp index 5aaeb459e..07be1608e 100644 --- a/components/sceneutil/visitor.cpp +++ b/components/sceneutil/visitor.cpp @@ -1,5 +1,7 @@ #include "visitor.hpp" +#include + #include #include @@ -67,8 +69,91 @@ namespace SceneUtil traverse(trans); } - void HideDrawablesVisitor::apply(osg::Drawable& drawable) + void RemoveVisitor::remove() { - drawable.setNodeMask(0); + 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; + } + } + + void CleanObjectRootVisitor::apply(osg::Drawable& drw) + { + applyDrawable(drw); + } + + void CleanObjectRootVisitor::apply(osg::Group& node) + { + applyNode(node); + } + + void CleanObjectRootVisitor::apply(osg::MatrixTransform& node) + { + applyNode(node); + } + + void CleanObjectRootVisitor::apply(osg::Node& node) + { + applyNode(node); + } + + void CleanObjectRootVisitor::applyNode(osg::Node& node) + { + if (node.getStateSet()) + node.setStateSet(NULL); + + if (node.getNodeMask() == 0x1 && node.getNumParents() == 1) + mToRemove.push_back(std::make_pair(&node, node.getParent(0))); + else + traverse(node); + } + + void CleanObjectRootVisitor::applyDrawable(osg::Node& node) + { + osg::NodePath::iterator parent = getNodePath().end()-2; + // We know that the parent is a Group because only Groups can have children. + osg::Group* parentGroup = static_cast(*parent); + + // Try to prune nodes that would be empty after the removal + if (parent != getNodePath().begin()) + { + // This could be extended to remove the parent's parent, and so on if they are empty as well. + // But for NIF files, there won't be a benefit since only TriShapes can be set to STATIC dataVariance. + osg::Group* parentParent = static_cast(*(parent - 1)); + if (parentGroup->getNumChildren() == 1 && parentGroup->getDataVariance() == osg::Object::STATIC) + { + mToRemove.push_back(std::make_pair(parentGroup, parentParent)); + return; + } + } + + mToRemove.push_back(std::make_pair(&node, parentGroup)); + } + + void RemoveTriBipVisitor::apply(osg::Drawable& drw) + { + applyImpl(drw); + } + + void RemoveTriBipVisitor::apply(osg::Group& node) + { + traverse(node); + } + + void RemoveTriBipVisitor::apply(osg::MatrixTransform& node) + { + traverse(node); + } + + void RemoveTriBipVisitor::applyImpl(osg::Node& node) + { + const std::string toFind = "tri bip"; + if (Misc::StringUtils::ciCompareLen(node.getName(), toFind, toFind.size()) == 0) + { + osg::Group* parent = static_cast(*(getNodePath().end()-2)); + // Not safe to remove in apply(), since the visitor is still iterating the child list + mToRemove.push_back(std::make_pair(&node, parent)); + } } } diff --git a/components/sceneutil/visitor.hpp b/components/sceneutil/visitor.hpp index bd3945296..3e9a565c0 100644 --- a/components/sceneutil/visitor.hpp +++ b/components/sceneutil/visitor.hpp @@ -68,7 +68,8 @@ namespace SceneUtil NodeMapVisitor(NodeMap& map) : osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) , mMap(map) - {} + { + } void apply(osg::MatrixTransform& trans); @@ -76,18 +77,47 @@ namespace SceneUtil NodeMap& mMap; }; - /// Hides all attached drawables - class HideDrawablesVisitor : public osg::NodeVisitor + /// @brief Base class for visitors that remove nodes from a scene graph. + /// Subclasses need to fill the mToRemove vector. + /// To use, node->accept(removeVisitor); removeVisitor.remove(); + class RemoveVisitor : public osg::NodeVisitor { public: - HideDrawablesVisitor() + RemoveVisitor() : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) { } - void apply(osg::Drawable& drawable) override; + void remove(); + + protected: + // + typedef std::vector > RemoveVec; + std::vector > mToRemove; }; + // Removes all drawables from a graph. + class CleanObjectRootVisitor : public RemoveVisitor + { + public: + virtual void apply(osg::Drawable& drw); + virtual void apply(osg::Group& node); + virtual void apply(osg::MatrixTransform& node); + virtual void apply(osg::Node& node); + + void applyNode(osg::Node& node); + void applyDrawable(osg::Node& node); + }; + + class RemoveTriBipVisitor : public RemoveVisitor + { + public: + virtual void apply(osg::Drawable& drw); + virtual void apply(osg::Group& node); + virtual void apply(osg::MatrixTransform& node); + + void applyImpl(osg::Node& node); + }; } #endif From 6b42f37918271cdd41000e141e8f597b69b82f5b Mon Sep 17 00:00:00 2001 From: Kyle Cooley Date: Sun, 22 Jul 2018 15:38:30 -0500 Subject: [PATCH 083/163] Handle creatures too --- apps/opencs/view/render/actor.cpp | 284 ++++++++++++++++------------- apps/opencs/view/render/actor.hpp | 8 + apps/opencs/view/render/object.cpp | 5 +- 3 files changed, 167 insertions(+), 130 deletions(-) diff --git a/apps/opencs/view/render/actor.cpp b/apps/opencs/view/render/actor.cpp index 93fff270a..3691085e3 100644 --- a/apps/opencs/view/render/actor.cpp +++ b/apps/opencs/view/render/actor.cpp @@ -18,6 +18,8 @@ namespace CSVRender { + const std::string Actor::MeshPrefix = "meshes\\"; + Actor::Actor(const std::string& id, int type, CSMWorld::Data& data) : mId(id) , mType(type) @@ -34,7 +36,42 @@ namespace CSVRender void Actor::update() { - const std::string MeshPrefix = "meshes\\"; + try + { + mBaseNode->removeChildren(0, mBaseNode->getNumChildren()); + + if (mType == CSMWorld::UniversalId::Type_Npc) + updateNpc(); + else if (mType == CSMWorld::UniversalId::Type_Creature) + updateCreature(); + } + catch (std::exception& e) + { + std::cout << "Caught exception: " << e.what() << std::endl; + } + } + + void Actor::updateCreature() + { + auto& referenceables = mData.getReferenceables(); + + auto& creature = dynamic_cast& >(referenceables.getRecord(mId)).get(); + + std::string skeletonModel = MeshPrefix + creature.mModel; + skeletonModel = Misc::ResourceHelpers::correctActorModelPath(skeletonModel, mData.getResourceSystem()->getVFS()); + loadSkeleton(skeletonModel); + + SceneUtil::RemoveTriBipVisitor removeTriBipVisitor; + mSkeleton->accept(removeTriBipVisitor); + removeTriBipVisitor.remove(); + + // Post setup + mSkeleton->markDirty(); + mSkeleton->setActive(SceneUtil::Skeleton::Active); + } + + void Actor::updateNpc() + { const unsigned int FemaleFlag = ESM::BodyPart::BPF_Female; auto& bodyParts = mData.getBodyParts(); @@ -42,144 +79,137 @@ namespace CSVRender auto& referenceables = mData.getReferenceables(); auto sceneMgr = mData.getResourceSystem()->getSceneManager(); + auto& npc = dynamic_cast& >(referenceables.getRecord(mId)).get(); + auto& race = dynamic_cast& >(races.getRecord(npc.mRace)).get(); - // Remove children - mBaseNode->removeChildren(0, mBaseNode->getNumChildren()); + bool is1stPerson = false; + bool isFemale = !npc.isMale(); + bool isBeast = race.mData.mFlags & ESM::Race::Beast; + bool isWerewolf = false; - try - { - // Npcs and creatures are handled differently - if (mType == CSMWorld::UniversalId::Type_Npc) + // Load skeleton + std::string skeletonModel = SceneUtil::getActorSkeleton(is1stPerson, isFemale, isBeast, isWerewolf); + skeletonModel = Misc::ResourceHelpers::correctActorModelPath(skeletonModel, mData.getResourceSystem()->getVFS()); + loadSkeleton(skeletonModel); + + // Get rid of the extra attachments + SceneUtil::CleanObjectRootVisitor cleanVisitor; + mSkeleton->accept(cleanVisitor); + cleanVisitor.remove(); + + // Map bone names to bones + SceneUtil::NodeMapVisitor::NodeMap nodeMap; + SceneUtil::NodeMapVisitor nmVisitor(nodeMap); + mSkeleton->accept(nmVisitor); + + using BPRaceKey = std::tuple; + using RaceToBPMap = std::map; + // Convenience method to generate a map from body part + race to mesh name + auto genRaceToBodyPartMap = [&](RaceToBPMap& bpMap) { + int size = bodyParts.getSize(); + for (int i = 0; i < size; ++i) { - auto& npc = dynamic_cast& >(referenceables.getRecord(mId)).get(); - auto& race = dynamic_cast& >(races.getRecord(npc.mRace)).get(); - - bool is1stPerson = false; - bool isFemale = !npc.isMale(); - bool isBeast = race.mData.mFlags & ESM::Race::Beast; - bool isWerewolf = false; - - // Load skeleton - std::string skeletonModel = SceneUtil::getActorSkeleton(is1stPerson, isFemale, isBeast, isWerewolf); - skeletonModel = Misc::ResourceHelpers::correctActorModelPath(skeletonModel, mData.getResourceSystem()->getVFS()); + auto& record = bodyParts.getRecord(i); + if (!record.isDeleted()) { - osg::ref_ptr temp = sceneMgr->getInstance(skeletonModel); - mSkeleton = dynamic_cast(temp.get()); - if (!mSkeleton) - { - mSkeleton = new SceneUtil::Skeleton(); - mSkeleton->addChild(temp); - } - mBaseNode->addChild(mSkeleton); + // Method to check if 1st person part or not + auto is1stPersonPart = [](std::string name) { + return name.size() >= 4 && name.find(".1st", name.size() - 4) != std::string::npos; + }; + + auto& bodyPart = record.get(); + if (bodyPart.mData.mType != ESM::BodyPart::MT_Skin || is1stPersonPart(bodyPart.mId)) + continue; + + bpMap.emplace( + BPRaceKey(bodyPart.mData.mPart, bodyPart.mData.mFlags & FemaleFlag ? 1 : 0, bodyPart.mRace), + MeshPrefix + bodyPart.mModel); } + } + }; - // Map bone names to bones - SceneUtil::NodeMapVisitor::NodeMap nodeMap; - SceneUtil::NodeMapVisitor nmVisitor(nodeMap); - mSkeleton->accept(nmVisitor); + // Generate mapping + RaceToBPMap r2bpMap; + genRaceToBodyPartMap(r2bpMap); - // Female mesh has some drawables attached, get rid of them - SceneUtil::CleanObjectRootVisitor cleanVisitor; - mSkeleton->accept(cleanVisitor); - cleanVisitor.remove(); - - // Convenience method to retrieve the mesh name of a body part - auto getBodyPartMesh = [&](std::string bpName) -> std::string { - int index = bodyParts.searchId(bpName); - if (index != -1 && !bodyParts.getRecord(index).isDeleted()) - return MeshPrefix + bodyParts.getRecord(index).get().mModel; - else - return ""; - }; - - using BPRaceKey = std::tuple; - using RaceToBPMap = std::map; - // Convenience method to generate a map from body part + race to mesh name - auto genRaceToBodyPartMap = [&](RaceToBPMap& bpMap) { - int size = bodyParts.getSize(); - for (int i = 0; i < size; ++i) - { - auto& record = bodyParts.getRecord(i); - if (!record.isDeleted()) - { - // Method to check if 1st person part or not - auto is1stPersonPart = [](std::string name) { - return name.size() >= 4 && name.find(".1st", name.size() - 4) != std::string::npos; - }; - - auto& bodyPart = record.get(); - if (bodyPart.mData.mType != ESM::BodyPart::MT_Skin || is1stPersonPart(bodyPart.mId)) - continue; - - bpMap.emplace( - BPRaceKey(bodyPart.mData.mPart, bodyPart.mData.mFlags & FemaleFlag ? 1 : 0, bodyPart.mRace), - MeshPrefix + bodyPart.mModel); - } - } - }; - - // Generate mapping - RaceToBPMap r2bpMap; - genRaceToBodyPartMap(r2bpMap); - - // Convenience method to add a body part - auto addBodyPart = [&](ESM::PartReferenceType type, std::string mesh) { - // Retrieve mesh name if necessary - if (mesh.empty()) - { - auto meshResult = r2bpMap.find(BPRaceKey(ESM::getMeshPart(type), isFemale ? 1 : 0, npc.mRace)); - if (meshResult != r2bpMap.end()) - { - mesh = meshResult->second; - } - else if (isFemale){ - meshResult = r2bpMap.find(BPRaceKey(ESM::getMeshPart(type), 0, npc.mRace)); - if (meshResult != r2bpMap.end()) - mesh = meshResult->second; - } - } - - // Attach to skeleton - std::string boneName = ESM::getBoneName(type); - auto node = nodeMap.find(boneName); - if (!mesh.empty() && node != nodeMap.end()) - { - auto instance = sceneMgr->getInstance(mesh); - SceneUtil::attach(instance, mSkeleton, boneName, node->second); - } - }; - - // Add body parts - for (unsigned int i = 0; i < ESM::PRT_Count; ++i) + // Convenience method to add a body part + auto addBodyPart = [&](ESM::PartReferenceType type, std::string mesh) { + // Retrieve mesh name if necessary + if (mesh.empty()) + { + auto meshResult = r2bpMap.find(BPRaceKey(ESM::getMeshPart(type), isFemale ? 1 : 0, npc.mRace)); + if (meshResult != r2bpMap.end()) { - auto part = static_cast(i); - switch (part) - { - case ESM::PRT_Head: - addBodyPart(part, getBodyPartMesh(npc.mHead)); - break; - case ESM::PRT_Hair: - addBodyPart(part, getBodyPartMesh(npc.mHair)); - break; - case ESM::PRT_Skirt: - case ESM::PRT_Shield: - case ESM::PRT_RPauldron: - case ESM::PRT_LPauldron: - case ESM::PRT_Weapon: - // No body part mesh associated - break; - default: - addBodyPart(part, ""); - } + mesh = meshResult->second; } - // Post setup - mSkeleton->markDirty(); - mSkeleton->setActive(SceneUtil::Skeleton::Active); + else if (isFemale){ + meshResult = r2bpMap.find(BPRaceKey(ESM::getMeshPart(type), 0, npc.mRace)); + if (meshResult != r2bpMap.end()) + mesh = meshResult->second; + } + } + + // Attach to skeleton + std::string boneName = ESM::getBoneName(type); + auto node = nodeMap.find(boneName); + if (!mesh.empty() && node != nodeMap.end()) + { + auto instance = sceneMgr->getInstance(mesh); + SceneUtil::attach(instance, mSkeleton, boneName, node->second); + } + }; + + // Add body parts + for (unsigned int i = 0; i < ESM::PRT_Count; ++i) + { + auto part = static_cast(i); + switch (part) + { + case ESM::PRT_Head: + addBodyPart(part, getBodyPartMesh(npc.mHead)); + break; + case ESM::PRT_Hair: + addBodyPart(part, getBodyPartMesh(npc.mHair)); + break; + case ESM::PRT_Skirt: + case ESM::PRT_Shield: + case ESM::PRT_RPauldron: + case ESM::PRT_LPauldron: + case ESM::PRT_Weapon: + // No body part mesh associated + break; + default: + addBodyPart(part, ""); } } - catch (std::exception& e) + + // Post setup + mSkeleton->markDirty(); + mSkeleton->setActive(SceneUtil::Skeleton::Active); + } + + void Actor::loadSkeleton(const std::string& model) + { + auto sceneMgr = mData.getResourceSystem()->getSceneManager(); + + osg::ref_ptr temp = sceneMgr->getInstance(model); + mSkeleton = dynamic_cast(temp.get()); + if (!mSkeleton) { - std::cout << "Caught exception: " << e.what() << std::endl; + mSkeleton = new SceneUtil::Skeleton(); + mSkeleton->addChild(temp); } + mBaseNode->addChild(mSkeleton); + } + + std::string Actor::getBodyPartMesh(const std::string& bodyPartId) + { + const auto& bodyParts = mData.getBodyParts(); + + int index = bodyParts.searchId(bodyPartId); + if (index != -1 && !bodyParts.getRecord(index).isDeleted()) + return MeshPrefix + bodyParts.getRecord(index).get().mModel; + else + return ""; } } diff --git a/apps/opencs/view/render/actor.hpp b/apps/opencs/view/render/actor.hpp index 10f4de558..b4e65ff2b 100644 --- a/apps/opencs/view/render/actor.hpp +++ b/apps/opencs/view/render/actor.hpp @@ -39,6 +39,14 @@ namespace CSVRender void update(); private: + void loadSkeleton(const std::string& model); + void updateCreature(); + void updateNpc(); + + std::string getBodyPartMesh(const std::string& bodyPartId); + + static const std::string MeshPrefix; + std::string mId; int mType; CSMWorld::Data& mData; diff --git a/apps/opencs/view/render/object.cpp b/apps/opencs/view/render/object.cpp index b8e171650..dbfd595b6 100644 --- a/apps/opencs/view/render/object.cpp +++ b/apps/opencs/view/render/object.cpp @@ -132,9 +132,8 @@ void CSVRender::Object::update() { try { - if (recordType == CSMWorld::UniversalId::Type_Npc) + if (recordType == CSMWorld::UniversalId::Type_Npc || recordType == CSMWorld::UniversalId::Type_Creature) { - std::cout << "recordType: Npc\n"; Actor actor(mReferenceableId, recordType, mData); actor.update(); mBaseNode->addChild(actor.getBaseNode()); @@ -147,8 +146,8 @@ void CSVRender::Object::update() } catch (std::exception& e) { - // TODO: use error marker mesh Log(Debug::Error) << e.what(); + mBaseNode->addChild(createErrorCube()); } } From 97ac0a92dd430c6a7b17a2b812fde52ecbc5b010 Mon Sep 17 00:00:00 2001 From: Kyle Cooley Date: Sat, 28 Jul 2018 12:23:43 -0500 Subject: [PATCH 084/163] Move data handling out of rendering code, equip armor/clothes --- apps/opencs/CMakeLists.txt | 1 + apps/opencs/model/world/actoradapter.cpp | 242 +++++++++++++++++++++++ apps/opencs/model/world/actoradapter.hpp | 80 ++++++++ apps/opencs/model/world/data.cpp | 12 ++ apps/opencs/model/world/data.hpp | 7 + apps/opencs/view/render/actor.cpp | 129 ++++-------- apps/opencs/view/render/actor.hpp | 11 +- 7 files changed, 389 insertions(+), 93 deletions(-) create mode 100644 apps/opencs/model/world/actoradapter.cpp create mode 100644 apps/opencs/model/world/actoradapter.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 26713f925..999324c51 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -19,6 +19,7 @@ opencs_hdrs_noqt (model/doc opencs_units (model/world idtable idtableproxymodel regionmap data commanddispatcher idtablebase resourcetable nestedtableproxymodel idtree infotableproxymodel landtexturetableproxymodel + actoradapter ) diff --git a/apps/opencs/model/world/actoradapter.cpp b/apps/opencs/model/world/actoradapter.cpp new file mode 100644 index 000000000..249908bb0 --- /dev/null +++ b/apps/opencs/model/world/actoradapter.cpp @@ -0,0 +1,242 @@ +#include "actoradapter.hpp" + +#include + +#include +#include +#include +#include +#include + +#include "data.hpp" + +namespace CSMWorld +{ + ActorAdapter::ActorAdapter(CSMWorld::Data& data) + : mReferenceables(data.getReferenceables()) + , mRaces(data.getRaces()) + , mBodyParts(data.getBodyParts()) + { + connect(data.getTableModel(UniversalId::Type_Referenceable), SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), + this, SLOT(handleReferenceableChanged(const QModelIndex&, const QModelIndex&))); + connect(data.getTableModel(UniversalId::Type_Race), SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), + this, SLOT(handleRaceChanged(const QModelIndex&, const QModelIndex&))); + connect(data.getTableModel(UniversalId::Type_BodyPart), SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), + this, SLOT(handleBodyPartChanged(const QModelIndex&, const QModelIndex&))); + } + + const ActorAdapter::ActorPartMap* ActorAdapter::getActorPartMap(const std::string& refId) + { + auto it = mActorPartMaps.find(refId); + if (it != mActorPartMaps.end()) + { + return &it->second; + } + else + { + updateActor(refId); + it = mActorPartMaps.find(refId); + if (it != mActorPartMaps.end()) + return &it->second; + } + + return nullptr; + } + + void ActorAdapter::handleReferenceableChanged(const QModelIndex& topLeft, const QModelIndex& botRight) + { + // TODO + } + + void ActorAdapter::handleRaceChanged(const QModelIndex& topLeft, const QModelIndex& botRight) + { + // TODO + } + + void ActorAdapter::handleBodyPartChanged(const QModelIndex& topLeft, const QModelIndex& botRight) + { + // TODO + } + + ActorAdapter::RacePartMap& ActorAdapter::getOrCreateRacePartMap(const std::string& raceId, bool isFemale) + { + auto key = std::make_pair(raceId, isFemale); + auto it = mRacePartMaps.find(key); + if (it != mRacePartMaps.end()) + { + return it->second; + } + else + { + // Create and find result + updateRaceParts(raceId); + return mRacePartMaps.find(key)->second; + } + } + + void ActorAdapter::updateRaceParts(const std::string& raceId) + { + // Convenience function to determine if part is for 1st person view + auto is1stPersonPart = [](std::string name) { + return name.size() >= 4 && name.find(".1st", name.size() - 4) != std::string::npos; + }; + + RacePartMap maleMap, femaleMap; + for (int i = 0; i < mBodyParts.getSize(); ++i) + { + auto& record = mBodyParts.getRecord(i); + if (!record.isDeleted() && record.get().mRace == raceId && record.get().mData.mType == ESM::BodyPart::MT_Skin && !is1stPersonPart(record.get().mId)) + { + auto& part = record.get(); + auto type = (ESM::BodyPart::MeshPart) part.mData.mPart; + // Note: Prefer the first part encountered for duplicates. emplace() does not overwrite + if (part.mData.mFlags & ESM::BodyPart::BPF_Female) + femaleMap.emplace(type, part.mId); + else + maleMap.emplace(type, part.mId); + } + } + + mRacePartMaps[std::make_pair(raceId, true)] = femaleMap; + mRacePartMaps[std::make_pair(raceId, false)] = maleMap; + } + + void ActorAdapter::updateActor(const std::string& refId) + { + int index = mReferenceables.searchId(refId); + if (index != -1) + { + int typeColumn = mReferenceables.findColumnIndex(CSMWorld::Columns::ColumnId_RecordType); + int recordType = mReferenceables.getData(index, typeColumn).toInt(); + if (recordType == CSMWorld::UniversalId::Type_Creature) + updateCreature(refId); + else if (recordType == CSMWorld::UniversalId::Type_Npc) + updateNpc(refId); + } + } + + void ActorAdapter::updateNpc(const std::string& refId) + { + auto& record = mReferenceables.getRecord(refId); + if (record.isDeleted()) + { + mActorPartMaps.erase(refId); + return; + } + + auto& npc = dynamic_cast&>(record).get(); + auto& femaleRacePartMap = getOrCreateRacePartMap(npc.mRace, true); + auto& maleRacePartMap = getOrCreateRacePartMap(npc.mRace, false); + + ActorPartMap npcMap; + + // Look at the npc's inventory first + for (auto& item : npc.mInventory.mList) + { + if (item.mCount > 0) + { + std::string itemId = item.mItem.toString(); + // Handle armor, weapons, and clothing + int index = mReferenceables.searchId(itemId); + if (index != -1 && !mReferenceables.getRecord(index).isDeleted()) + { + auto& itemRecord = mReferenceables.getRecord(index); + + int typeColumn = mReferenceables.findColumnIndex(CSMWorld::Columns::ColumnId_RecordType); + int recordType = mReferenceables.getData(index, typeColumn).toInt(); + if (recordType == CSMWorld::UniversalId::Type_Armor) + { + auto& armor = dynamic_cast&>(itemRecord).get(); + for (auto& part : armor.mParts.mParts) + { + std::string bodyPartId; + if (!npc.isMale()) + bodyPartId = part.mFemale; + if (bodyPartId.empty()) + bodyPartId = part.mMale; + + if (!bodyPartId.empty()) + npcMap.emplace(static_cast(part.mPart), bodyPartId); + } + } + else if (recordType == CSMWorld::UniversalId::Type_Clothing) + { + auto& clothing = dynamic_cast&>(itemRecord).get(); + for (auto& part : clothing.mParts.mParts) + { + std::string bodyPartId; + if (!npc.isMale()) + bodyPartId = part.mFemale; + if (bodyPartId.empty()) + bodyPartId = part.mMale; + + if (!bodyPartId.empty()) + npcMap.emplace(static_cast(part.mPart), bodyPartId); + } + } + else if (recordType == CSMWorld::UniversalId::Type_Weapon) + { + // TODO + } + } + } + } + + // Fill in the rest with body parts + for (int i = 0; i < ESM::PRT_Count; ++i) + { + auto type = static_cast(i); + if (npcMap.find(type) == npcMap.end()) + { + switch (type) + { + case ESM::PRT_Head: + npcMap.emplace(type, npc.mHead); + break; + case ESM::PRT_Hair: + npcMap.emplace(type, npc.mHair); + break; + case ESM::PRT_Skirt: + case ESM::PRT_Shield: + case ESM::PRT_RPauldron: + case ESM::PRT_LPauldron: + case ESM::PRT_Weapon: + // No body part associated + break; + default: + { + std::string bodyPartId; + // Check female map if applicable + if (!npc.isMale()) + { + auto partIt = femaleRacePartMap.find(ESM::getMeshPart(type)); + if (partIt != femaleRacePartMap.end()) + bodyPartId = partIt->second; + } + + // Check male map next + if (bodyPartId.empty() || npc.isMale()) + { + auto partIt = maleRacePartMap.find(ESM::getMeshPart(type)); + if (partIt != maleRacePartMap.end()) + bodyPartId = partIt->second; + } + + // Add to map + if (!bodyPartId.empty()) + { + npcMap.emplace(type, bodyPartId); + } + } + } + } + } + + mActorPartMaps[refId] = npcMap; + } + + void ActorAdapter::updateCreature(const std::string& refId) + { + // TODO + } +} diff --git a/apps/opencs/model/world/actoradapter.hpp b/apps/opencs/model/world/actoradapter.hpp new file mode 100644 index 000000000..2c94f9c2c --- /dev/null +++ b/apps/opencs/model/world/actoradapter.hpp @@ -0,0 +1,80 @@ +#ifndef CSM_WOLRD_ACTORADAPTER_H +#define CSM_WOLRD_ACTORADAPTER_H + +#include +#include +#include + +#include + +#include +#include + +#include "refidcollection.hpp" +#include "idcollection.hpp" + +namespace ESM +{ + struct Race; + enum PartReferenceType; +} + +namespace CSMWorld +{ + class Data; + + /// Quick and dirty hashing functor. + struct StringBoolPairHash + { + size_t operator()(const std::pair& value) const noexcept + { + auto stringHash = std::hash(); + return stringHash(value.first) + value.second; + } + }; + + class ActorAdapter : public QObject + { + Q_OBJECT + public: + + // Maps body part type to 'body part' id + using ActorPartMap = std::unordered_map; + // Maps mesh part type to 'body part' id + using RacePartMap = std::unordered_map; + + ActorAdapter(CSMWorld::Data& data); + + const ActorPartMap* getActorPartMap(const std::string& refId); + + signals: + + void actorChanged(const std::string& refId); + + public slots: + + void handleReferenceableChanged(const QModelIndex&, const QModelIndex&); + void handleRaceChanged(const QModelIndex&, const QModelIndex&); + void handleBodyPartChanged(const QModelIndex&, const QModelIndex&); + + private: + + RacePartMap& getOrCreateRacePartMap(const std::string& raceId, bool isFemale); + + void updateRaceParts(const std::string& raceId); + void updateActor(const std::string& refId); + void updateNpc(const std::string& refId); + void updateCreature(const std::string& refId); + + RefIdCollection& mReferenceables; + IdCollection& mRaces; + IdCollection& mBodyParts; + + // Key: referenceable id + std::unordered_map mActorPartMaps; + // Key: race id, is female + std::unordered_map, RacePartMap, StringBoolPairHash> mRacePartMaps; + }; +} + +#endif diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index 053754943..f3f897a29 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -572,6 +572,8 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, bool fsStrict, const Files::Pat UniversalId::Type_Video); addModel (new IdTable (&mMetaData), UniversalId::Type_MetaData); + mActorAdapter.reset(new ActorAdapter(*this)); + mRefLoadCache.clear(); // clear here rather than startLoading() and continueLoading() for multiple content files } @@ -912,6 +914,16 @@ QAbstractItemModel *CSMWorld::Data::getTableModel (const CSMWorld::UniversalId& return iter->second; } +const CSMWorld::ActorAdapter* CSMWorld::Data::getActorAdapter() const +{ + return mActorAdapter.get(); +} + +CSMWorld::ActorAdapter* CSMWorld::Data::getActorAdapter() +{ + return mActorAdapter.get(); +} + void CSMWorld::Data::merge() { mGlobals.merge(); diff --git a/apps/opencs/model/world/data.hpp b/apps/opencs/model/world/data.hpp index 1b975f430..7c4d8885a 100644 --- a/apps/opencs/model/world/data.hpp +++ b/apps/opencs/model/world/data.hpp @@ -36,6 +36,7 @@ #include "../doc/stage.hpp" +#include "actoradapter.hpp" #include "idcollection.hpp" #include "nestedidcollection.hpp" #include "universalid.hpp" @@ -73,6 +74,7 @@ namespace ESM namespace CSMWorld { + class ActorAdapter; class ResourcesManager; class Resources; @@ -110,6 +112,7 @@ namespace CSMWorld RefCollection mRefs; IdCollection mFilters; Collection mMetaData; + std::unique_ptr mActorAdapter; const Fallback::Map* mFallbackMap; std::vector mModels; std::map mModelIndex; @@ -287,6 +290,10 @@ namespace CSMWorld /// \note The returned table may either be the model for the ID itself or the model that /// contains the record specified by the ID. + const ActorAdapter* getActorAdapter() const; + + ActorAdapter* getActorAdapter(); + void merge(); ///< Merge modified into base. diff --git a/apps/opencs/view/render/actor.cpp b/apps/opencs/view/render/actor.cpp index 3691085e3..b816c1504 100644 --- a/apps/opencs/view/render/actor.cpp +++ b/apps/opencs/view/render/actor.cpp @@ -12,7 +12,6 @@ #include #include #include -#include #include "../../model/world/data.hpp" @@ -24,8 +23,8 @@ namespace CSVRender : mId(id) , mType(type) , mData(data) - , mSkeleton(nullptr) , mBaseNode(new osg::Group()) + , mSkeleton(nullptr) { } @@ -57,6 +56,7 @@ namespace CSVRender auto& creature = dynamic_cast& >(referenceables.getRecord(mId)).get(); + // Load skeleton with meshes std::string skeletonModel = MeshPrefix + creature.mModel; skeletonModel = Misc::ResourceHelpers::correctActorModelPath(skeletonModel, mData.getResourceSystem()->getVFS()); loadSkeleton(skeletonModel); @@ -65,6 +65,9 @@ namespace CSVRender mSkeleton->accept(removeTriBipVisitor); removeTriBipVisitor.remove(); + // Attach weapons + loadBodyParts(creature.mId); + // Post setup mSkeleton->markDirty(); mSkeleton->setActive(SceneUtil::Skeleton::Active); @@ -72,12 +75,8 @@ namespace CSVRender void Actor::updateNpc() { - const unsigned int FemaleFlag = ESM::BodyPart::BPF_Female; - - auto& bodyParts = mData.getBodyParts(); auto& races = mData.getRaces(); auto& referenceables = mData.getReferenceables(); - auto sceneMgr = mData.getResourceSystem()->getSceneManager(); auto& npc = dynamic_cast& >(referenceables.getRecord(mId)).get(); auto& race = dynamic_cast& >(races.getRecord(npc.mRace)).get(); @@ -97,91 +96,8 @@ namespace CSVRender mSkeleton->accept(cleanVisitor); cleanVisitor.remove(); - // Map bone names to bones - SceneUtil::NodeMapVisitor::NodeMap nodeMap; - SceneUtil::NodeMapVisitor nmVisitor(nodeMap); - mSkeleton->accept(nmVisitor); - - using BPRaceKey = std::tuple; - using RaceToBPMap = std::map; - // Convenience method to generate a map from body part + race to mesh name - auto genRaceToBodyPartMap = [&](RaceToBPMap& bpMap) { - int size = bodyParts.getSize(); - for (int i = 0; i < size; ++i) - { - auto& record = bodyParts.getRecord(i); - if (!record.isDeleted()) - { - // Method to check if 1st person part or not - auto is1stPersonPart = [](std::string name) { - return name.size() >= 4 && name.find(".1st", name.size() - 4) != std::string::npos; - }; - - auto& bodyPart = record.get(); - if (bodyPart.mData.mType != ESM::BodyPart::MT_Skin || is1stPersonPart(bodyPart.mId)) - continue; - - bpMap.emplace( - BPRaceKey(bodyPart.mData.mPart, bodyPart.mData.mFlags & FemaleFlag ? 1 : 0, bodyPart.mRace), - MeshPrefix + bodyPart.mModel); - } - } - }; - - // Generate mapping - RaceToBPMap r2bpMap; - genRaceToBodyPartMap(r2bpMap); - - // Convenience method to add a body part - auto addBodyPart = [&](ESM::PartReferenceType type, std::string mesh) { - // Retrieve mesh name if necessary - if (mesh.empty()) - { - auto meshResult = r2bpMap.find(BPRaceKey(ESM::getMeshPart(type), isFemale ? 1 : 0, npc.mRace)); - if (meshResult != r2bpMap.end()) - { - mesh = meshResult->second; - } - else if (isFemale){ - meshResult = r2bpMap.find(BPRaceKey(ESM::getMeshPart(type), 0, npc.mRace)); - if (meshResult != r2bpMap.end()) - mesh = meshResult->second; - } - } - - // Attach to skeleton - std::string boneName = ESM::getBoneName(type); - auto node = nodeMap.find(boneName); - if (!mesh.empty() && node != nodeMap.end()) - { - auto instance = sceneMgr->getInstance(mesh); - SceneUtil::attach(instance, mSkeleton, boneName, node->second); - } - }; - - // Add body parts - for (unsigned int i = 0; i < ESM::PRT_Count; ++i) - { - auto part = static_cast(i); - switch (part) - { - case ESM::PRT_Head: - addBodyPart(part, getBodyPartMesh(npc.mHead)); - break; - case ESM::PRT_Hair: - addBodyPart(part, getBodyPartMesh(npc.mHair)); - break; - case ESM::PRT_Skirt: - case ESM::PRT_Shield: - case ESM::PRT_RPauldron: - case ESM::PRT_LPauldron: - case ESM::PRT_Weapon: - // No body part mesh associated - break; - default: - addBodyPart(part, ""); - } - } + // Attach parts to skeleton + loadBodyParts(npc.mId); // Post setup mSkeleton->markDirty(); @@ -200,6 +116,37 @@ namespace CSVRender mSkeleton->addChild(temp); } mBaseNode->addChild(mSkeleton); + + // Map bone names to bones + mNodeMap.clear(); + SceneUtil::NodeMapVisitor nmVisitor(mNodeMap); + mSkeleton->accept(nmVisitor); + + } + + void Actor::loadBodyParts(const std::string& actorId) + { + auto actorAdapter = mData.getActorAdapter(); + auto partMap = actorAdapter->getActorPartMap(actorId); + if (partMap) + { + for (auto& pair : *partMap) + attachBodyPart(pair.first, getBodyPartMesh(pair.second)); + } + } + + void Actor::attachBodyPart(ESM::PartReferenceType type, const std::string& mesh) + { + auto sceneMgr = mData.getResourceSystem()->getSceneManager(); + + // Attach to skeleton + std::string boneName = ESM::getBoneName(type); + auto node = mNodeMap.find(boneName); + if (!mesh.empty() && node != mNodeMap.end()) + { + auto instance = sceneMgr->getInstance(mesh); + SceneUtil::attach(instance, mSkeleton, boneName, node->second); + } } std::string Actor::getBodyPartMesh(const std::string& bodyPartId) diff --git a/apps/opencs/view/render/actor.hpp b/apps/opencs/view/render/actor.hpp index b4e65ff2b..4f809c172 100644 --- a/apps/opencs/view/render/actor.hpp +++ b/apps/opencs/view/render/actor.hpp @@ -5,6 +5,9 @@ #include +#include +#include + namespace osg { class Group; @@ -39,10 +42,13 @@ namespace CSVRender void update(); private: - void loadSkeleton(const std::string& model); void updateCreature(); void updateNpc(); + void loadSkeleton(const std::string& model); + void loadBodyParts(const std::string& actorId); + void attachBodyPart(ESM::PartReferenceType, const std::string& mesh); + std::string getBodyPartMesh(const std::string& bodyPartId); static const std::string MeshPrefix; @@ -51,8 +57,9 @@ namespace CSVRender int mType; CSMWorld::Data& mData; - SceneUtil::Skeleton* mSkeleton; osg::ref_ptr mBaseNode; + SceneUtil::Skeleton* mSkeleton; + SceneUtil::NodeMapVisitor::NodeMap mNodeMap; }; } From 6bece13a32ff40b69ed4d9acf380c210d00a70e8 Mon Sep 17 00:00:00 2001 From: Kyle Cooley Date: Tue, 21 Aug 2018 15:41:05 -0400 Subject: [PATCH 085/163] Use new Log class for error message --- apps/opencs/view/render/actor.cpp | 5 ++--- components/sceneutil/visitor.cpp | 6 +++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/apps/opencs/view/render/actor.cpp b/apps/opencs/view/render/actor.cpp index b816c1504..a61d31de6 100644 --- a/apps/opencs/view/render/actor.cpp +++ b/apps/opencs/view/render/actor.cpp @@ -1,10 +1,9 @@ #include "actor.hpp" -#include - #include #include +#include #include #include #include @@ -46,7 +45,7 @@ namespace CSVRender } catch (std::exception& e) { - std::cout << "Caught exception: " << e.what() << std::endl; + Log(Debug::Error) << "Exception in Actor::update(): " << e.what(); } } diff --git a/components/sceneutil/visitor.cpp b/components/sceneutil/visitor.cpp index 07be1608e..536331132 100644 --- a/components/sceneutil/visitor.cpp +++ b/components/sceneutil/visitor.cpp @@ -1,12 +1,12 @@ #include "visitor.hpp" -#include - #include #include #include +#include + #include namespace SceneUtil @@ -74,7 +74,7 @@ namespace SceneUtil 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(); } } From b2115b60e6710c07b6209e241036fc2e8e006748 Mon Sep 17 00:00:00 2001 From: Kyle Cooley Date: Tue, 21 Aug 2018 15:54:21 -0400 Subject: [PATCH 086/163] Fix qt4 build --- apps/opencs/model/world/actoradapter.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/opencs/model/world/actoradapter.hpp b/apps/opencs/model/world/actoradapter.hpp index 2c94f9c2c..09bd2682b 100644 --- a/apps/opencs/model/world/actoradapter.hpp +++ b/apps/opencs/model/world/actoradapter.hpp @@ -6,6 +6,7 @@ #include #include +#include #include #include From 1276e0fa9b2e2f7ac5e1a67d288c79479e536fc8 Mon Sep 17 00:00:00 2001 From: Kyle Cooley Date: Thu, 23 Aug 2018 22:40:43 -0400 Subject: [PATCH 087/163] Handle changes to race record when rendering actors --- apps/opencs/CMakeLists.txt | 4 +- apps/opencs/model/world/actoradapter.cpp | 66 ++++++++++++++++++++---- apps/opencs/model/world/actoradapter.hpp | 7 +++ apps/opencs/view/render/actor.cpp | 18 ++++++- apps/opencs/view/render/actor.hpp | 9 +++- apps/opencs/view/render/object.cpp | 7 +-- apps/opencs/view/render/object.hpp | 3 ++ 7 files changed, 98 insertions(+), 16 deletions(-) diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 999324c51..b0bd95eb9 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -89,12 +89,12 @@ opencs_units (view/render scenewidget worldspacewidget pagedworldspacewidget unpagedworldspacewidget previewwidget editmode instancemode instanceselectionmode instancemovemode orbitcameramode pathgridmode selectionmode pathgridselectionmode cameracontroller - cellwater terraintexturemode + cellwater terraintexturemode actor ) opencs_units_noqt (view/render lighting lightingday lightingnight lightingbright object cell terrainstorage tagbase - cellarrow cellmarker cellborder pathgrid actor + cellarrow cellmarker cellborder pathgrid ) opencs_hdrs_noqt (view/render diff --git a/apps/opencs/model/world/actoradapter.cpp b/apps/opencs/model/world/actoradapter.cpp index 249908bb0..9c397879e 100644 --- a/apps/opencs/model/world/actoradapter.cpp +++ b/apps/opencs/model/world/actoradapter.cpp @@ -1,7 +1,6 @@ #include "actoradapter.hpp" -#include - +#include #include #include #include @@ -45,17 +44,56 @@ namespace CSMWorld void ActorAdapter::handleReferenceableChanged(const QModelIndex& topLeft, const QModelIndex& botRight) { - // TODO + // Setup + const int TypeColumn = mReferenceables.findColumnIndex(CSMWorld::Columns::ColumnId_RecordType); + int rowStart = getHighestIndex(topLeft).row(); + int rowEnd = getHighestIndex(botRight).row(); + + // Handle each record + for (int row = rowStart; row <= rowEnd; ++row) + { + int type = mReferenceables.getData(row, TypeColumn).toInt(); + if (type == CSMWorld::UniversalId::Type_Creature || type == CSMWorld::UniversalId::Type_Npc) + { + // Update the cached npc or creature + std::string refId = mReferenceables.getId(row); + if (mActorPartMaps.find(refId) != mActorPartMaps.end()) + updateActor(refId); + } + else if (type == CSMWorld::UniversalId::Type_Armor) + { + // TODO update everything? + // store all items referenced when creating map and check against that here + } + else if (type == CSMWorld::UniversalId::Type_Clothing) + { + // TODO update everything? + } + } } void ActorAdapter::handleRaceChanged(const QModelIndex& topLeft, const QModelIndex& botRight) { - // TODO + int rowStart = getHighestIndex(topLeft).row(); + int rowEnd = getHighestIndex(botRight).row(); + for (int row = rowStart; row <= rowEnd; ++row) + { + std::string raceId = mRaces.getId(row); + updateNpcsWithRace(raceId); + } } void ActorAdapter::handleBodyPartChanged(const QModelIndex& topLeft, const QModelIndex& botRight) { // TODO + Log(Debug::Info) << "Body Part Changed (" << topLeft.row() << ", " << topLeft.column() << ") (" << botRight.row() << ", " << botRight.column() << ")"; + } + + QModelIndex ActorAdapter::getHighestIndex(QModelIndex index) const + { + while (index.parent().isValid()) + index = index.parent(); + return index; } ActorAdapter::RacePartMap& ActorAdapter::getOrCreateRacePartMap(const std::string& raceId, bool isFemale) @@ -174,10 +212,6 @@ namespace CSMWorld npcMap.emplace(static_cast(part.mPart), bodyPartId); } } - else if (recordType == CSMWorld::UniversalId::Type_Weapon) - { - // TODO - } } } } @@ -233,10 +267,24 @@ namespace CSMWorld } mActorPartMaps[refId] = npcMap; + emit actorChanged(refId); } void ActorAdapter::updateCreature(const std::string& refId) { - // TODO + emit actorChanged(refId); + } + + void ActorAdapter::updateNpcsWithRace(const std::string& raceId) + { + for (auto it : mActorPartMaps) + { + auto& refId = it.first; + auto& npc = dynamic_cast&>(mReferenceables.getRecord(refId)).get(); + if (npc.mRace == raceId) + { + updateNpc(refId); + } + } } } diff --git a/apps/opencs/model/world/actoradapter.hpp b/apps/opencs/model/world/actoradapter.hpp index 09bd2682b..ad308fc02 100644 --- a/apps/opencs/model/world/actoradapter.hpp +++ b/apps/opencs/model/world/actoradapter.hpp @@ -60,6 +60,11 @@ namespace CSMWorld private: + ActorAdapter(const ActorAdapter&) = delete; + ActorAdapter& operator=(const ActorAdapter&) = delete; + + QModelIndex getHighestIndex(QModelIndex) const; + RacePartMap& getOrCreateRacePartMap(const std::string& raceId, bool isFemale); void updateRaceParts(const std::string& raceId); @@ -67,6 +72,8 @@ namespace CSMWorld void updateNpc(const std::string& refId); void updateCreature(const std::string& refId); + void updateNpcsWithRace(const std::string& raceId); + RefIdCollection& mReferenceables; IdCollection& mRaces; IdCollection& mBodyParts; diff --git a/apps/opencs/view/render/actor.cpp b/apps/opencs/view/render/actor.cpp index a61d31de6..6d663ad5e 100644 --- a/apps/opencs/view/render/actor.cpp +++ b/apps/opencs/view/render/actor.cpp @@ -20,6 +20,7 @@ namespace CSVRender Actor::Actor(const std::string& id, int type, CSMWorld::Data& data) : mId(id) + , mInitialized(false) , mType(type) , mData(data) , mBaseNode(new osg::Group()) @@ -45,7 +46,22 @@ namespace CSVRender } catch (std::exception& e) { - Log(Debug::Error) << "Exception in Actor::update(): " << e.what(); + Log(Debug::Info) << "Exception in Actor::update(): " << e.what(); + } + + if (!mInitialized) + { + mInitialized = true; + connect(mData.getActorAdapter(), SIGNAL(actorChanged(const std::string&)), this, SLOT(handleActorChanged(const std::string&))); + } + } + + void Actor::handleActorChanged(const std::string& refId) + { + if (mId == refId) + { + Log(Debug::Info) << "Actor::actorChanged " << mId; + update(); } } diff --git a/apps/opencs/view/render/actor.hpp b/apps/opencs/view/render/actor.hpp index 4f809c172..42a6019ed 100644 --- a/apps/opencs/view/render/actor.hpp +++ b/apps/opencs/view/render/actor.hpp @@ -5,6 +5,8 @@ #include +#include + #include #include @@ -26,8 +28,9 @@ namespace SceneUtil namespace CSVRender { /// Handles loading an npc or creature - class Actor + class Actor : public QObject { + Q_OBJECT public: /// Creates an actor. /// \param id The referenceable id @@ -41,6 +44,9 @@ namespace CSVRender /// (Re)creates the npc or creature renderable void update(); + private slots: + void handleActorChanged(const std::string& refId); + private: void updateCreature(); void updateNpc(); @@ -54,6 +60,7 @@ namespace CSVRender static const std::string MeshPrefix; std::string mId; + bool mInitialized; int mType; CSMWorld::Data& mData; diff --git a/apps/opencs/view/render/object.cpp b/apps/opencs/view/render/object.cpp index dbfd595b6..63f1bc8a8 100644 --- a/apps/opencs/view/render/object.cpp +++ b/apps/opencs/view/render/object.cpp @@ -134,9 +134,10 @@ void CSVRender::Object::update() { if (recordType == CSMWorld::UniversalId::Type_Npc || recordType == CSMWorld::UniversalId::Type_Creature) { - Actor actor(mReferenceableId, recordType, mData); - actor.update(); - mBaseNode->addChild(actor.getBaseNode()); + if (!mActor) + mActor.reset(new Actor(mReferenceableId, recordType, mData)); + mActor->update(); + mBaseNode->addChild(mActor->getBaseNode()); } else { diff --git a/apps/opencs/view/render/object.hpp b/apps/opencs/view/render/object.hpp index 3e54093d3..10a46fc10 100644 --- a/apps/opencs/view/render/object.hpp +++ b/apps/opencs/view/render/object.hpp @@ -1,6 +1,7 @@ #ifndef OPENCS_VIEW_OBJECT_H #define OPENCS_VIEW_OBJECT_H +#include #include #include @@ -41,6 +42,7 @@ namespace CSMWorld namespace CSVRender { + class Actor; class Object; // An object to attach as user data to the osg::Node, allows us to get an Object back from a Node when we are doing a ray query @@ -98,6 +100,7 @@ namespace CSVRender osg::ref_ptr mMarker[3]; int mSubMode; float mMarkerTransparency; + std::unique_ptr mActor; /// Not implemented Object (const Object&); From 2a9ebac57225407d3ff991d81964d9e7ceeb0941 Mon Sep 17 00:00:00 2001 From: Kyle Cooley Date: Fri, 24 Aug 2018 02:44:02 -0400 Subject: [PATCH 088/163] Simplify update logic, update when race parts are changed. --- apps/opencs/model/world/actoradapter.cpp | 207 +++++++++++++++-------- apps/opencs/model/world/actoradapter.hpp | 35 +++- apps/opencs/view/render/actor.cpp | 6 +- 3 files changed, 168 insertions(+), 80 deletions(-) diff --git a/apps/opencs/model/world/actoradapter.cpp b/apps/opencs/model/world/actoradapter.cpp index 9c397879e..4626d1642 100644 --- a/apps/opencs/model/world/actoradapter.cpp +++ b/apps/opencs/model/world/actoradapter.cpp @@ -24,22 +24,22 @@ namespace CSMWorld this, SLOT(handleBodyPartChanged(const QModelIndex&, const QModelIndex&))); } - const ActorAdapter::ActorPartMap* ActorAdapter::getActorPartMap(const std::string& refId) + const ActorAdapter::ActorPartMap* ActorAdapter::getActorParts(const std::string& refId, bool create) { - auto it = mActorPartMaps.find(refId); - if (it != mActorPartMaps.end()) + auto it = mCachedActors.find(refId); + if (it != mCachedActors.end()) { - return &it->second; + return &it->second.parts; + } + else if (create) + { + updateActor(refId); + return getActorParts(refId, false); } else { - updateActor(refId); - it = mActorPartMaps.find(refId); - if (it != mActorPartMaps.end()) - return &it->second; + return nullptr; } - - return nullptr; } void ActorAdapter::handleReferenceableChanged(const QModelIndex& topLeft, const QModelIndex& botRight) @@ -57,17 +57,13 @@ namespace CSMWorld { // Update the cached npc or creature std::string refId = mReferenceables.getId(row); - if (mActorPartMaps.find(refId) != mActorPartMaps.end()) + if (mCachedActors.find(refId) != mCachedActors.end()) updateActor(refId); } - else if (type == CSMWorld::UniversalId::Type_Armor) + else if (type == CSMWorld::UniversalId::Type_Armor || type == CSMWorld::UniversalId::Type_Clothing) { - // TODO update everything? - // store all items referenced when creating map and check against that here - } - else if (type == CSMWorld::UniversalId::Type_Clothing) - { - // TODO update everything? + std::string refId = mReferenceables.getId(row); + updateActorsWithDependency(refId); } } } @@ -79,14 +75,28 @@ namespace CSMWorld for (int row = rowStart; row <= rowEnd; ++row) { std::string raceId = mRaces.getId(row); - updateNpcsWithRace(raceId); + updateActorsWithDependency(raceId); } } void ActorAdapter::handleBodyPartChanged(const QModelIndex& topLeft, const QModelIndex& botRight) { - // TODO - Log(Debug::Info) << "Body Part Changed (" << topLeft.row() << ", " << topLeft.column() << ") (" << botRight.row() << ", " << botRight.column() << ")"; + int rowStart = getHighestIndex(topLeft).row(); + int rowEnd = getHighestIndex(botRight).row(); + for (int row = rowStart; row <= rowEnd; ++row) + { + // Manually update race specified by part + auto& record = mBodyParts.getRecord(row); + if (!record.isDeleted()) + { + updateRace(record.get().mRace); + } + + // Update entries with a tracked dependency + std::string partId = mBodyParts.getId(row); + updateRacesWithDependency(partId); + updateActorsWithDependency(partId); + } } QModelIndex ActorAdapter::getHighestIndex(QModelIndex index) const @@ -96,47 +106,66 @@ namespace CSMWorld return index; } - ActorAdapter::RacePartMap& ActorAdapter::getOrCreateRacePartMap(const std::string& raceId, bool isFemale) + bool ActorAdapter::is1stPersonPart(const std::string& name) const { - auto key = std::make_pair(raceId, isFemale); - auto it = mRacePartMaps.find(key); - if (it != mRacePartMaps.end()) + return name.size() >= 4 && name.find(".1st", name.size() - 4) != std::string::npos; + } + + ActorAdapter::RaceData& ActorAdapter::getRaceData(const std::string& raceId) + { + auto it = mCachedRaces.find(raceId); + if (it != mCachedRaces.end()) { return it->second; } else { // Create and find result - updateRaceParts(raceId); - return mRacePartMaps.find(key)->second; + updateRace(raceId); + return mCachedRaces.find(raceId)->second; } } - void ActorAdapter::updateRaceParts(const std::string& raceId) + void ActorAdapter::updateRace(const std::string& raceId) { - // Convenience function to determine if part is for 1st person view - auto is1stPersonPart = [](std::string name) { - return name.size() >= 4 && name.find(".1st", name.size() - 4) != std::string::npos; - }; + // Retrieve or create cache entry + auto raceDataIt = mCachedRaces.find(raceId); + if (raceDataIt == mCachedRaces.end()) + { + auto result = mCachedRaces.emplace(raceId, RaceData()); + raceDataIt = result.first; + } - RacePartMap maleMap, femaleMap; + auto& raceData = raceDataIt->second; + raceData.femaleParts.clear(); + raceData.maleParts.clear(); + raceData.dependencies.clear(); + + // Construct entry for (int i = 0; i < mBodyParts.getSize(); ++i) { auto& record = mBodyParts.getRecord(i); - if (!record.isDeleted() && record.get().mRace == raceId && record.get().mData.mType == ESM::BodyPart::MT_Skin && !is1stPersonPart(record.get().mId)) + if (!record.isDeleted() && record.get().mRace == raceId) { auto& part = record.get(); - auto type = (ESM::BodyPart::MeshPart) part.mData.mPart; - // Note: Prefer the first part encountered for duplicates. emplace() does not overwrite - if (part.mData.mFlags & ESM::BodyPart::BPF_Female) - femaleMap.emplace(type, part.mId); - else - maleMap.emplace(type, part.mId); + + // Part could affect race data + raceData.dependencies.emplace(part.mId, true); + + // Add base types + if (part.mData.mType == ESM::BodyPart::MT_Skin && !is1stPersonPart(part.mId)) + { + auto type = (ESM::BodyPart::MeshPart) part.mData.mPart; + // Note: Prefer the first part encountered for duplicates. emplace() does not overwrite + if (part.mData.mFlags & ESM::BodyPart::BPF_Female) + raceData.femaleParts.emplace(type, part.mId); + else + raceData.maleParts.emplace(type, part.mId); + } } } - mRacePartMaps[std::make_pair(raceId, true)] = femaleMap; - mRacePartMaps[std::make_pair(raceId, false)] = maleMap; + updateActorsWithDependency(raceId); } void ActorAdapter::updateActor(const std::string& refId) @@ -156,17 +185,28 @@ namespace CSMWorld void ActorAdapter::updateNpc(const std::string& refId) { auto& record = mReferenceables.getRecord(refId); + + // Retrieve record if possible if (record.isDeleted()) { - mActorPartMaps.erase(refId); + mCachedActors.erase(refId); + emit actorChanged(refId); return; } - auto& npc = dynamic_cast&>(record).get(); - auto& femaleRacePartMap = getOrCreateRacePartMap(npc.mRace, true); - auto& maleRacePartMap = getOrCreateRacePartMap(npc.mRace, false); - ActorPartMap npcMap; + // Create holder for cached data + auto actorIt = mCachedActors.find(refId); + if (actorIt == mCachedActors.end()) + { + auto result = mCachedActors.emplace(refId, ActorData()); + actorIt = result.first; + } + auto& actorData = actorIt->second; + + // Reset old data + actorData.parts.clear(); + actorData.dependencies.clear(); // Look at the npc's inventory first for (auto& item : npc.mInventory.mList) @@ -174,7 +214,7 @@ namespace CSMWorld if (item.mCount > 0) { std::string itemId = item.mItem.toString(); - // Handle armor, weapons, and clothing + // Handle armor and clothing int index = mReferenceables.searchId(itemId); if (index != -1 && !mReferenceables.getRecord(index).isDeleted()) { @@ -184,6 +224,10 @@ namespace CSMWorld int recordType = mReferenceables.getData(index, typeColumn).toInt(); if (recordType == CSMWorld::UniversalId::Type_Armor) { + // Changes here could affect the actor + actorData.dependencies.emplace(itemId, true); + + // Add any parts if there is room auto& armor = dynamic_cast&>(itemRecord).get(); for (auto& part : armor.mParts.mParts) { @@ -194,11 +238,18 @@ namespace CSMWorld bodyPartId = part.mMale; if (!bodyPartId.empty()) - npcMap.emplace(static_cast(part.mPart), bodyPartId); + { + actorData.parts.emplace(static_cast(part.mPart), bodyPartId); + actorData.dependencies.emplace(bodyPartId, true); + } } } else if (recordType == CSMWorld::UniversalId::Type_Clothing) { + // Changes here could affect the actor + actorData.dependencies.emplace(itemId, true); + + // Add any parts if there is room auto& clothing = dynamic_cast&>(itemRecord).get(); for (auto& part : clothing.mParts.mParts) { @@ -209,26 +260,37 @@ namespace CSMWorld bodyPartId = part.mMale; if (!bodyPartId.empty()) - npcMap.emplace(static_cast(part.mPart), bodyPartId); + { + actorData.parts.emplace(static_cast(part.mPart), bodyPartId); + actorData.dependencies.emplace(bodyPartId, true); + } } } } } } - // Fill in the rest with body parts + // Lookup cached race parts + auto& raceData = getRaceData(npc.mRace); + + // Changes to race could affect the actor + actorData.dependencies.emplace(npc.mRace, true); + + // Fill in the rest with race specific body parts for (int i = 0; i < ESM::PRT_Count; ++i) { auto type = static_cast(i); - if (npcMap.find(type) == npcMap.end()) + if (actorData.parts.find(type) == actorData.parts.end()) { switch (type) { case ESM::PRT_Head: - npcMap.emplace(type, npc.mHead); + actorData.parts.emplace(type, npc.mHead); + actorData.dependencies.emplace(npc.mHead, true); break; case ESM::PRT_Hair: - npcMap.emplace(type, npc.mHair); + actorData.parts.emplace(type, npc.mHair); + actorData.dependencies.emplace(npc.mHair, true); break; case ESM::PRT_Skirt: case ESM::PRT_Shield: @@ -243,48 +305,57 @@ namespace CSMWorld // Check female map if applicable if (!npc.isMale()) { - auto partIt = femaleRacePartMap.find(ESM::getMeshPart(type)); - if (partIt != femaleRacePartMap.end()) + auto partIt = raceData.femaleParts.find(ESM::getMeshPart(type)); + if (partIt != raceData.femaleParts.end()) bodyPartId = partIt->second; } // Check male map next if (bodyPartId.empty() || npc.isMale()) { - auto partIt = maleRacePartMap.find(ESM::getMeshPart(type)); - if (partIt != maleRacePartMap.end()) + auto partIt = raceData.maleParts.find(ESM::getMeshPart(type)); + if (partIt != raceData.maleParts.end()) bodyPartId = partIt->second; } // Add to map if (!bodyPartId.empty()) { - npcMap.emplace(type, bodyPartId); + actorData.parts.emplace(type, bodyPartId); + actorData.dependencies.emplace(bodyPartId, true); } } } } } - mActorPartMaps[refId] = npcMap; + // Signal change to actor emit actorChanged(refId); } void ActorAdapter::updateCreature(const std::string& refId) { + // Signal change to actor emit actorChanged(refId); } - void ActorAdapter::updateNpcsWithRace(const std::string& raceId) + void ActorAdapter::updateActorsWithDependency(const std::string& id) { - for (auto it : mActorPartMaps) + for (auto it : mCachedActors) { - auto& refId = it.first; - auto& npc = dynamic_cast&>(mReferenceables.getRecord(refId)).get(); - if (npc.mRace == raceId) - { - updateNpc(refId); - } + auto& deps = it.second.dependencies; + if (deps.find(id) != deps.end()) + updateActor(it.first); + } + } + + void ActorAdapter::updateRacesWithDependency(const std::string& id) + { + for (auto it : mCachedRaces) + { + auto& deps = it.second.dependencies; + if (deps.find(id) != deps.end()) + updateRace(it.first); } } } diff --git a/apps/opencs/model/world/actoradapter.hpp b/apps/opencs/model/world/actoradapter.hpp index ad308fc02..f1148199c 100644 --- a/apps/opencs/model/world/actoradapter.hpp +++ b/apps/opencs/model/world/actoradapter.hpp @@ -41,12 +41,10 @@ namespace CSMWorld // Maps body part type to 'body part' id using ActorPartMap = std::unordered_map; - // Maps mesh part type to 'body part' id - using RacePartMap = std::unordered_map; ActorAdapter(CSMWorld::Data& data); - const ActorPartMap* getActorPartMap(const std::string& refId); + const ActorPartMap* getActorParts(const std::string& refId, bool create=true); signals: @@ -59,29 +57,48 @@ namespace CSMWorld void handleBodyPartChanged(const QModelIndex&, const QModelIndex&); private: + // Maps mesh part type to 'body part' id + using RacePartMap = std::unordered_map; + // Stores ids that are referenced by the actor. Data part is meaningless. + using DependencyMap = std::unordered_map; + + struct ActorData + { + ActorPartMap parts; + DependencyMap dependencies; + }; + + struct RaceData + { + RacePartMap femaleParts; + RacePartMap maleParts; + DependencyMap dependencies; + }; ActorAdapter(const ActorAdapter&) = delete; ActorAdapter& operator=(const ActorAdapter&) = delete; QModelIndex getHighestIndex(QModelIndex) const; + bool is1stPersonPart(const std::string& id) const; - RacePartMap& getOrCreateRacePartMap(const std::string& raceId, bool isFemale); + RaceData& getRaceData(const std::string& raceId); - void updateRaceParts(const std::string& raceId); + void updateRace(const std::string& raceId); void updateActor(const std::string& refId); void updateNpc(const std::string& refId); void updateCreature(const std::string& refId); - void updateNpcsWithRace(const std::string& raceId); + void updateActorsWithDependency(const std::string& id); + void updateRacesWithDependency(const std::string& id); RefIdCollection& mReferenceables; IdCollection& mRaces; IdCollection& mBodyParts; // Key: referenceable id - std::unordered_map mActorPartMaps; - // Key: race id, is female - std::unordered_map, RacePartMap, StringBoolPairHash> mRacePartMaps; + std::unordered_map mCachedActors; + // Key: race id + std::unordered_map mCachedRaces; }; } diff --git a/apps/opencs/view/render/actor.cpp b/apps/opencs/view/render/actor.cpp index 6d663ad5e..b9979ae2c 100644 --- a/apps/opencs/view/render/actor.cpp +++ b/apps/opencs/view/render/actor.cpp @@ -142,10 +142,10 @@ namespace CSVRender void Actor::loadBodyParts(const std::string& actorId) { auto actorAdapter = mData.getActorAdapter(); - auto partMap = actorAdapter->getActorPartMap(actorId); - if (partMap) + auto parts = actorAdapter->getActorParts(actorId); + if (parts) { - for (auto& pair : *partMap) + for (auto& pair : *parts) attachBodyPart(pair.first, getBodyPartMesh(pair.second)); } } From 031502b2ab80216d9dc4ef475a1ad6cb9ad2092e Mon Sep 17 00:00:00 2001 From: Kyle Cooley Date: Sun, 26 Aug 2018 01:13:50 -0400 Subject: [PATCH 089/163] Reorganize ActorAdapter data, use weak cache for sharing --- apps/opencs/model/world/actoradapter.cpp | 715 +++++++++++++++-------- apps/opencs/model/world/actoradapter.hpp | 187 ++++-- apps/opencs/view/render/actor.cpp | 14 +- apps/opencs/view/render/actor.hpp | 3 + components/CMakeLists.txt | 4 + components/cache/weakcache.hpp | 116 ++++ 6 files changed, 714 insertions(+), 325 deletions(-) create mode 100644 components/cache/weakcache.hpp diff --git a/apps/opencs/model/world/actoradapter.cpp b/apps/opencs/model/world/actoradapter.cpp index 4626d1642..b3a1216f7 100644 --- a/apps/opencs/model/world/actoradapter.cpp +++ b/apps/opencs/model/world/actoradapter.cpp @@ -11,72 +11,290 @@ namespace CSMWorld { - ActorAdapter::ActorAdapter(CSMWorld::Data& data) + const std::string& ActorAdapter::RaceData::getId() const + { + return mId; + } + + bool ActorAdapter::RaceData::handlesPart(ESM::PartReferenceType type) const + { + switch (type) + { + case ESM::PRT_Skirt: + case ESM::PRT_Shield: + case ESM::PRT_RPauldron: + case ESM::PRT_LPauldron: + case ESM::PRT_Weapon: + return false; + default: + return true; + } + } + + const std::string& ActorAdapter::RaceData::getFemalePart(ESM::PartReferenceType index) const + { + return mFemaleParts[ESM::getMeshPart(index)]; + } + + const std::string& ActorAdapter::RaceData::getMalePart(ESM::PartReferenceType index) const + { + return mMaleParts[ESM::getMeshPart(index)]; + } + + bool ActorAdapter::RaceData::hasDependency(const std::string& id) const + { + return mDependencies.find(id) != mDependencies.end(); + } + + void ActorAdapter::RaceData::setFemalePart(ESM::BodyPart::MeshPart index, const std::string& partId) + { + mFemaleParts[index] = partId; + addOtherDependency(partId); + } + + void ActorAdapter::RaceData::setMalePart(ESM::BodyPart::MeshPart index, const std::string& partId) + { + mMaleParts[index] = partId; + addOtherDependency(partId); + } + + void ActorAdapter::RaceData::addOtherDependency(const std::string& id) + { + if (!id.empty()) mDependencies.emplace(id); + } + + void ActorAdapter::RaceData::reset(const std::string& id) + { + mId = id; + for (auto& str : mFemaleParts) + str.clear(); + for (auto& str : mMaleParts) + str.clear(); + mDependencies.clear(); + + // Mark self as a dependency + addOtherDependency(id); + } + + + const std::string& ActorAdapter::ActorData::getId() const + { + return mId; + } + + bool ActorAdapter::ActorData::isFemale() const + { + return mFemale; + } + + const std::string& ActorAdapter::ActorData::getPart(ESM::PartReferenceType index) const + { + if (mParts[index].empty() && mRaceData && mRaceData->handlesPart(index)) + { + return mFemale ? mRaceData->getFemalePart(index) : mRaceData->getMalePart(index); + } + return mParts[index]; + } + + bool ActorAdapter::ActorData::hasDependency(const std::string& id) const + { + return mDependencies.find(id) != mDependencies.end(); + } + + void ActorAdapter::ActorData::setPart(ESM::PartReferenceType index, const std::string& partId) + { + mParts[index] = partId; + addOtherDependency(partId); + } + + void ActorAdapter::ActorData::addOtherDependency(const std::string& id) + { + if (!id.empty()) mDependencies.emplace(id); + } + + void ActorAdapter::ActorData::reset(const std::string& id, bool isFemale, RaceDataPtr raceData) + { + mId = id; + mFemale = isFemale; + mRaceData = raceData; + for (auto& str : mParts) + str.clear(); + mDependencies.clear(); + + // Mark self and race as a dependency + addOtherDependency(id); + if (raceData) addOtherDependency(raceData->getId()); + } + + + ActorAdapter::ActorAdapter(Data& data) : mReferenceables(data.getReferenceables()) , mRaces(data.getRaces()) , mBodyParts(data.getBodyParts()) { - connect(data.getTableModel(UniversalId::Type_Referenceable), SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), + // Setup qt slots and signals + QAbstractItemModel* refModel = data.getTableModel(UniversalId::Type_Referenceable); + connect(refModel, SIGNAL(rowsInserted(const QModelIndex&, int, int)), + this, SLOT(handleReferenceablesInserted(const QModelIndex&, int, int))); + connect(refModel, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), this, SLOT(handleReferenceableChanged(const QModelIndex&, const QModelIndex&))); - connect(data.getTableModel(UniversalId::Type_Race), SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), + connect(refModel, SIGNAL(rowsAboutToBeRemoved(const QModelIndex&, int, int)), + this, SLOT(handleReferenceablesAboutToBeRemoved(const QModelIndex&, int, int))); + + QAbstractItemModel* raceModel = data.getTableModel(UniversalId::Type_Race); + connect(raceModel, SIGNAL(rowsInserted(const QModelIndex&, int, int)), + this, SLOT(handleRacesAboutToBeRemoved(const QModelIndex&, int, int))); + connect(raceModel, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), this, SLOT(handleRaceChanged(const QModelIndex&, const QModelIndex&))); - connect(data.getTableModel(UniversalId::Type_BodyPart), SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), + connect(raceModel, SIGNAL(rowsAboutToBeRemoved(const QModelIndex&, int, int)), + this, SLOT(handleRacesAboutToBeRemoved(const QModelIndex&, int, int))); + + QAbstractItemModel* partModel = data.getTableModel(UniversalId::Type_BodyPart); + connect(partModel, SIGNAL(rowsInserted(const QModelIndex&, int, int)), + this, SLOT(handleBodyPartsInserted(const QModelIndex&, int, int))); + connect(partModel, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), this, SLOT(handleBodyPartChanged(const QModelIndex&, const QModelIndex&))); + connect(partModel, SIGNAL(rowsAboutToBeRemoved(const QModelIndex&, int, int)), + this, SLOT(handleBodyPartsAboutToBeRemoved(const QModelIndex&, int, int))); } - const ActorAdapter::ActorPartMap* ActorAdapter::getActorParts(const std::string& refId, bool create) + ActorAdapter::ActorDataPtr ActorAdapter::getActorData(const std::string& id) { - auto it = mCachedActors.find(refId); - if (it != mCachedActors.end()) + // Return cached actor data if it exists + ActorDataPtr data = mCachedActors.get(id); + if (data) { - return &it->second.parts; + return data; } - else if (create) + + // Create the actor data + data.reset(new ActorData()); + setupActor(id, data); + mCachedActors.insert(id, data); + return data; + } + + void ActorAdapter::handleReferenceablesInserted(const QModelIndex& parent, int start, int end) + { + // Only rows added at the top level are pertinent. Others are caught by dataChanged handler. + if (!parent.isValid()) { - updateActor(refId); - return getActorParts(refId, false); - } - else - { - return nullptr; + for (int row = start; row <= end; ++row) + { + std::string refId = mReferenceables.getId(row); + markDirtyDependency(refId); + } } + + // Update affected + updateDirty(); } void ActorAdapter::handleReferenceableChanged(const QModelIndex& topLeft, const QModelIndex& botRight) { - // Setup - const int TypeColumn = mReferenceables.findColumnIndex(CSMWorld::Columns::ColumnId_RecordType); - int rowStart = getHighestIndex(topLeft).row(); - int rowEnd = getHighestIndex(botRight).row(); + int start = getHighestIndex(topLeft).row(); + int end = getHighestIndex(botRight).row(); // Handle each record - for (int row = rowStart; row <= rowEnd; ++row) + for (int row = start; row <= end; ++row) { - int type = mReferenceables.getData(row, TypeColumn).toInt(); - if (type == CSMWorld::UniversalId::Type_Creature || type == CSMWorld::UniversalId::Type_Npc) - { - // Update the cached npc or creature - std::string refId = mReferenceables.getId(row); - if (mCachedActors.find(refId) != mCachedActors.end()) - updateActor(refId); - } - else if (type == CSMWorld::UniversalId::Type_Armor || type == CSMWorld::UniversalId::Type_Clothing) + std::string refId = mReferenceables.getId(row); + markDirtyDependency(refId); + } + + // Update affected + updateDirty(); + } + + void ActorAdapter::handleReferenceablesAboutToBeRemoved(const QModelIndex& parent, int start, int end) + { + // Only rows at the top are pertinent. + if (!parent.isValid()) + { + for (int row = start; row <= end; ++row) { std::string refId = mReferenceables.getId(row); - updateActorsWithDependency(refId); + markDirtyDependency(refId); } } } + void ActorAdapter::handleReferenceablesRemoved(const QModelIndex& parent, int start, int end) + { + // Changes specified in handleReferenceablesAboutToBeRemoved + updateDirty(); + } + + void ActorAdapter::handleRacesInserted(const QModelIndex& parent, int start, int end) + { + // Only rows added at the top are pertinent. + if (!parent.isValid()) + { + for (int row = start; row <= end; ++row) + { + std::string raceId = mReferenceables.getId(row); + markDirtyDependency(raceId); + } + } + + // Update affected + updateDirty(); + } + void ActorAdapter::handleRaceChanged(const QModelIndex& topLeft, const QModelIndex& botRight) { - int rowStart = getHighestIndex(topLeft).row(); - int rowEnd = getHighestIndex(botRight).row(); - for (int row = rowStart; row <= rowEnd; ++row) + int start = getHighestIndex(topLeft).row(); + int end = getHighestIndex(botRight).row(); + for (int row = start; row <= end; ++row) { std::string raceId = mRaces.getId(row); - updateActorsWithDependency(raceId); + markDirtyDependency(raceId); } + + // Update affected + updateDirty(); + } + + void ActorAdapter::handleRacesAboutToBeRemoved(const QModelIndex& parent, int start, int end) + { + // Only changes at the top are pertinent. + if (!parent.isValid()) + { + for (int row = start; row <= end; ++row) + { + std::string raceId = mRaces.getId(row); + markDirtyDependency(raceId); + } + } + } + + void ActorAdapter::handleRacesRemoved(const QModelIndex& parent, int start, int end) + { + // Changes specified in handleRacesAboutToBeRemoved + updateDirty(); + } + + void ActorAdapter::handleBodyPartsInserted(const QModelIndex& parent, int start, int end) + { + // Only rows added at the top are pertinent. + if (!parent.isValid()) + { + for (int row = start; row <= end; ++row) + { + // Race specified by part may need update + auto& record = mBodyParts.getRecord(row); + if (!record.isDeleted()) + { + markDirtyDependency(record.get().mRace); + } + + std::string partId = mBodyParts.getId(row); + markDirtyDependency(partId); + } + } + + // Update affected + updateDirty(); } void ActorAdapter::handleBodyPartChanged(const QModelIndex& topLeft, const QModelIndex& botRight) @@ -85,18 +303,39 @@ namespace CSMWorld int rowEnd = getHighestIndex(botRight).row(); for (int row = rowStart; row <= rowEnd; ++row) { - // Manually update race specified by part + // Race specified by part may need update auto& record = mBodyParts.getRecord(row); if (!record.isDeleted()) { - updateRace(record.get().mRace); + markDirtyDependency(record.get().mRace); } // Update entries with a tracked dependency std::string partId = mBodyParts.getId(row); - updateRacesWithDependency(partId); - updateActorsWithDependency(partId); + markDirtyDependency(partId); } + + // Update affected + updateDirty(); + } + + void ActorAdapter::handleBodyPartsAboutToBeRemoved(const QModelIndex& parent, int start, int end) + { + // Only changes at the top are pertinent. + if (!parent.isValid()) + { + for (int row = start; row <= end; ++row) + { + std::string partId = mBodyParts.getId(row); + markDirtyDependency(partId); + } + } + } + + void ActorAdapter::handleBodyPartsRemoved(const QModelIndex& parent, int start, int end) + { + // Changes specified in handleBodyPartsAboutToBeRemoved + updateDirty(); } QModelIndex ActorAdapter::getHighestIndex(QModelIndex index) const @@ -111,251 +350,223 @@ namespace CSMWorld return name.size() >= 4 && name.find(".1st", name.size() - 4) != std::string::npos; } - ActorAdapter::RaceData& ActorAdapter::getRaceData(const std::string& raceId) + ActorAdapter::RaceDataPtr ActorAdapter::getRaceData(const std::string& id) { - auto it = mCachedRaces.find(raceId); - if (it != mCachedRaces.end()) + // Return cached race data if it exists + RaceDataPtr data = mCachedRaces.get(id); + if (data) return data; + + // Create the race data + data.reset(new RaceData()); + setupRace(id, data); + mCachedRaces.insert(id, data); + return data; + } + + void ActorAdapter::setupActor(const std::string& id, ActorDataPtr data) + { + int index = mReferenceables.searchId(id); + if (index == -1) { - return it->second; + // Record does not exist + data->reset(id); + emit actorChanged(id); + return; + } + + auto& record = mReferenceables.getRecord(index); + if (record.isDeleted()) + { + // Record is deleted and therefore not accessible + data->reset(id); + emit actorChanged(id); + return; + } + + const int TypeColumn = mReferenceables.findColumnIndex(Columns::ColumnId_RecordType); + int type = mReferenceables.getData(index, TypeColumn).toInt(); + if (type == UniversalId::Type_Creature) + { + // Valid creature record + setupCreature(id, data); + emit actorChanged(id); + } + else if (type == UniversalId::Type_Npc) + { + // Valid npc record + setupNpc(id, data); + emit actorChanged(id); } else { - // Create and find result - updateRace(raceId); - return mCachedRaces.find(raceId)->second; + // Wrong record type + data->reset(id); + emit actorChanged(id); } } - void ActorAdapter::updateRace(const std::string& raceId) + void ActorAdapter::setupRace(const std::string& id, RaceDataPtr data) { - // Retrieve or create cache entry - auto raceDataIt = mCachedRaces.find(raceId); - if (raceDataIt == mCachedRaces.end()) + // Common setup + data->reset(id); + + int index = mRaces.searchId(id); + if (index == -1) { - auto result = mCachedRaces.emplace(raceId, RaceData()); - raceDataIt = result.first; - } - - auto& raceData = raceDataIt->second; - raceData.femaleParts.clear(); - raceData.maleParts.clear(); - raceData.dependencies.clear(); - - // Construct entry - for (int i = 0; i < mBodyParts.getSize(); ++i) - { - auto& record = mBodyParts.getRecord(i); - if (!record.isDeleted() && record.get().mRace == raceId) - { - auto& part = record.get(); - - // Part could affect race data - raceData.dependencies.emplace(part.mId, true); - - // Add base types - if (part.mData.mType == ESM::BodyPart::MT_Skin && !is1stPersonPart(part.mId)) - { - auto type = (ESM::BodyPart::MeshPart) part.mData.mPart; - // Note: Prefer the first part encountered for duplicates. emplace() does not overwrite - if (part.mData.mFlags & ESM::BodyPart::BPF_Female) - raceData.femaleParts.emplace(type, part.mId); - else - raceData.maleParts.emplace(type, part.mId); - } - } - } - - updateActorsWithDependency(raceId); - } - - void ActorAdapter::updateActor(const std::string& refId) - { - int index = mReferenceables.searchId(refId); - if (index != -1) - { - int typeColumn = mReferenceables.findColumnIndex(CSMWorld::Columns::ColumnId_RecordType); - int recordType = mReferenceables.getData(index, typeColumn).toInt(); - if (recordType == CSMWorld::UniversalId::Type_Creature) - updateCreature(refId); - else if (recordType == CSMWorld::UniversalId::Type_Npc) - updateNpc(refId); - } - } - - void ActorAdapter::updateNpc(const std::string& refId) - { - auto& record = mReferenceables.getRecord(refId); - - // Retrieve record if possible - if (record.isDeleted()) - { - mCachedActors.erase(refId); - emit actorChanged(refId); + // Record does not exist return; } - auto& npc = dynamic_cast&>(record).get(); - // Create holder for cached data - auto actorIt = mCachedActors.find(refId); - if (actorIt == mCachedActors.end()) + auto& raceRecord = mRaces.getRecord(index); + if (raceRecord.isDeleted()) { - auto result = mCachedActors.emplace(refId, ActorData()); - actorIt = result.first; + // Record is deleted, so not accessible + return; } - auto& actorData = actorIt->second; - // Reset old data - actorData.parts.clear(); - actorData.dependencies.clear(); + // TODO move stuff in actor related to race here - // Look at the npc's inventory first + // Setup body parts + for (int i = 0; i < mBodyParts.getSize(); ++i) + { + std::string partId = mBodyParts.getId(i); + auto& partRecord = mBodyParts.getRecord(i); + + if (partRecord.isDeleted()) + { + // Record is deleted, so not accessible. + continue; + } + + auto& part = partRecord.get(); + if (part.mRace == id && part.mData.mType == ESM::BodyPart::MT_Skin && !is1stPersonPart(part.mId)) + { + auto type = (ESM::BodyPart::MeshPart) part.mData.mPart; + bool female = part.mData.mFlags & ESM::BodyPart::BPF_Female; + if (female) data->setFemalePart(type, part.mId); + else data->setMalePart(type, part.mId); + } + } + } + + void ActorAdapter::setupNpc(const std::string& id, ActorDataPtr data) + { + // Common setup, record is known to exist and is not deleted + int index = mReferenceables.searchId(id); + auto& npc = dynamic_cast&>(mReferenceables.getRecord(index)).get(); + + RaceDataPtr raceData = getRaceData(npc.mRace); + data->reset(id, !npc.isMale(), raceData); + + // Add inventory items for (auto& item : npc.mInventory.mList) { - if (item.mCount > 0) + if (item.mCount <= 0) continue; + std::string itemId = item.mItem.toString(); + addNpcItem(itemId, data); + } + + // Add head and hair + data->setPart(ESM::PRT_Head, npc.mHead); + data->setPart(ESM::PRT_Hair, npc.mHair); + } + + void ActorAdapter::addNpcItem(const std::string& itemId, ActorDataPtr data) + { + int index = mReferenceables.searchId(itemId); + if (index == -1) + { + // Item does not exist yet + data->addOtherDependency(itemId); + return; + } + + auto& record = mReferenceables.getRecord(index); + if (record.isDeleted()) + { + // Item cannot be accessed yet + data->addOtherDependency(itemId); + return; + } + + // Convenience function to add a parts list to actor data + auto addParts = [&](const ESM::PartReferenceList& list) { + for (auto& part : list.mParts) { - std::string itemId = item.mItem.toString(); - // Handle armor and clothing - int index = mReferenceables.searchId(itemId); - if (index != -1 && !mReferenceables.getRecord(index).isDeleted()) - { - auto& itemRecord = mReferenceables.getRecord(index); + std::string partId; + auto partType = (ESM::PartReferenceType) part.mPart; - int typeColumn = mReferenceables.findColumnIndex(CSMWorld::Columns::ColumnId_RecordType); - int recordType = mReferenceables.getData(index, typeColumn).toInt(); - if (recordType == CSMWorld::UniversalId::Type_Armor) - { - // Changes here could affect the actor - actorData.dependencies.emplace(itemId, true); + if (data->isFemale()) + partId = part.mFemale; + if (partId.empty()) + partId = part.mMale; - // Add any parts if there is room - auto& armor = dynamic_cast&>(itemRecord).get(); - for (auto& part : armor.mParts.mParts) - { - std::string bodyPartId; - if (!npc.isMale()) - bodyPartId = part.mFemale; - if (bodyPartId.empty()) - bodyPartId = part.mMale; + if (!partId.empty()) data->setPart(partType, partId); + } + }; - if (!bodyPartId.empty()) - { - actorData.parts.emplace(static_cast(part.mPart), bodyPartId); - actorData.dependencies.emplace(bodyPartId, true); - } - } - } - else if (recordType == CSMWorld::UniversalId::Type_Clothing) - { - // Changes here could affect the actor - actorData.dependencies.emplace(itemId, true); + int TypeColumn = mReferenceables.findColumnIndex(Columns::ColumnId_RecordType); + int type = mReferenceables.getData(index, TypeColumn).toInt(); + if (type == UniversalId::Type_Armor) + { + auto& armor = dynamic_cast&>(record).get(); + addParts(armor.mParts); - // Add any parts if there is room - auto& clothing = dynamic_cast&>(itemRecord).get(); - for (auto& part : clothing.mParts.mParts) - { - std::string bodyPartId; - if (!npc.isMale()) - bodyPartId = part.mFemale; - if (bodyPartId.empty()) - bodyPartId = part.mMale; + // Changing parts could affect what is picked for rendering + data->addOtherDependency(itemId); + } + else if (type == UniversalId::Type_Clothing) + { + auto& clothing = dynamic_cast&>(record).get(); + addParts(clothing.mParts); - if (!bodyPartId.empty()) - { - actorData.parts.emplace(static_cast(part.mPart), bodyPartId); - actorData.dependencies.emplace(bodyPartId, true); - } - } - } - } + // Changing parts could affect what is picked for rendering + data->addOtherDependency(itemId); + } + } + + void ActorAdapter::setupCreature(const std::string& id, ActorDataPtr data) + { + data->reset(id); + + // TODO move stuff from Actor here + } + + void ActorAdapter::markDirtyDependency(const std::string& dep) + { + for (auto raceIt : mCachedRaces) + { + if (raceIt->hasDependency(dep)) + mDirtyRaces.emplace(raceIt->getId()); + } + for (auto actorIt : mCachedActors) + { + if (actorIt->hasDependency(dep)) + mDirtyActors.emplace(actorIt->getId()); + } + } + + void ActorAdapter::updateDirty() + { + // Handle races before actors, since actors are dependent on race + for (auto& race : mDirtyRaces) + { + RaceDataPtr data = mCachedRaces.get(race); + if (data) + { + setupRace(race, data); } } + mDirtyRaces.clear(); - // Lookup cached race parts - auto& raceData = getRaceData(npc.mRace); - - // Changes to race could affect the actor - actorData.dependencies.emplace(npc.mRace, true); - - // Fill in the rest with race specific body parts - for (int i = 0; i < ESM::PRT_Count; ++i) + for (auto& actor : mDirtyActors) { - auto type = static_cast(i); - if (actorData.parts.find(type) == actorData.parts.end()) + ActorDataPtr data = mCachedActors.get(actor); + if (data) { - switch (type) - { - case ESM::PRT_Head: - actorData.parts.emplace(type, npc.mHead); - actorData.dependencies.emplace(npc.mHead, true); - break; - case ESM::PRT_Hair: - actorData.parts.emplace(type, npc.mHair); - actorData.dependencies.emplace(npc.mHair, true); - break; - case ESM::PRT_Skirt: - case ESM::PRT_Shield: - case ESM::PRT_RPauldron: - case ESM::PRT_LPauldron: - case ESM::PRT_Weapon: - // No body part associated - break; - default: - { - std::string bodyPartId; - // Check female map if applicable - if (!npc.isMale()) - { - auto partIt = raceData.femaleParts.find(ESM::getMeshPart(type)); - if (partIt != raceData.femaleParts.end()) - bodyPartId = partIt->second; - } - - // Check male map next - if (bodyPartId.empty() || npc.isMale()) - { - auto partIt = raceData.maleParts.find(ESM::getMeshPart(type)); - if (partIt != raceData.maleParts.end()) - bodyPartId = partIt->second; - } - - // Add to map - if (!bodyPartId.empty()) - { - actorData.parts.emplace(type, bodyPartId); - actorData.dependencies.emplace(bodyPartId, true); - } - } - } + setupActor(actor, data); } } - - // Signal change to actor - emit actorChanged(refId); - } - - void ActorAdapter::updateCreature(const std::string& refId) - { - // Signal change to actor - emit actorChanged(refId); - } - - void ActorAdapter::updateActorsWithDependency(const std::string& id) - { - for (auto it : mCachedActors) - { - auto& deps = it.second.dependencies; - if (deps.find(id) != deps.end()) - updateActor(it.first); - } - } - - void ActorAdapter::updateRacesWithDependency(const std::string& id) - { - for (auto it : mCachedRaces) - { - auto& deps = it.second.dependencies; - if (deps.find(id) != deps.end()) - updateRace(it.first); - } + mDirtyActors.clear(); } } diff --git a/apps/opencs/model/world/actoradapter.hpp b/apps/opencs/model/world/actoradapter.hpp index f1148199c..0e8b0c9fe 100644 --- a/apps/opencs/model/world/actoradapter.hpp +++ b/apps/opencs/model/world/actoradapter.hpp @@ -1,13 +1,13 @@ #ifndef CSM_WOLRD_ACTORADAPTER_H #define CSM_WOLRD_ACTORADAPTER_H -#include -#include -#include +#include +#include #include #include +#include #include #include @@ -17,88 +17,147 @@ namespace ESM { struct Race; - enum PartReferenceType; } namespace CSMWorld { class Data; - /// Quick and dirty hashing functor. - struct StringBoolPairHash - { - size_t operator()(const std::pair& value) const noexcept - { - auto stringHash = std::hash(); - return stringHash(value.first) + value.second; - } - }; - + /// Adapts multiple collections to provide the data needed to render + /// an npc or creature. class ActorAdapter : public QObject { - Q_OBJECT + Q_OBJECT + public: + + /// A list indexed by ESM::PartReferenceType + using ActorPartList = std::array; + /// A list indexed by ESM::BodyPart::MeshPart + using RacePartList = std::array; + /// Tracks unique strings + using StringSet = std::unordered_set; + + + /// Contains base race data shared between actors + class RaceData + { public: + /// Retrieves the id of the race represented + const std::string& getId() const; + /// Checks if a part could exist for the given type + bool handlesPart(ESM::PartReferenceType type) const; + /// Retrieves the associated body part + const std::string& getFemalePart(ESM::PartReferenceType index) const; + /// Retrieves the associated body part + const std::string& getMalePart(ESM::PartReferenceType index) const; + /// Checks if the race has a data dependency + bool hasDependency(const std::string& id) const; - // Maps body part type to 'body part' id - using ActorPartMap = std::unordered_map; - - ActorAdapter(CSMWorld::Data& data); - - const ActorPartMap* getActorParts(const std::string& refId, bool create=true); - - signals: - - void actorChanged(const std::string& refId); - - public slots: - - void handleReferenceableChanged(const QModelIndex&, const QModelIndex&); - void handleRaceChanged(const QModelIndex&, const QModelIndex&); - void handleBodyPartChanged(const QModelIndex&, const QModelIndex&); + /// Sets the associated part if it's empty and marks a dependency + void setFemalePart(ESM::BodyPart::MeshPart partIndex, const std::string& partId); + /// Sets the associated part if it's empty and marks a dependency + void setMalePart(ESM::BodyPart::MeshPart partIndex, const std::string& partId); + /// Marks an additional dependency + void addOtherDependency(const std::string& id); + /// Clears parts and dependencies + void reset(const std::string& raceId); private: - // Maps mesh part type to 'body part' id - using RacePartMap = std::unordered_map; - // Stores ids that are referenced by the actor. Data part is meaningless. - using DependencyMap = std::unordered_map; + bool handles(ESM::PartReferenceType type) const; + std::string mId; + RacePartList mFemaleParts; + RacePartList mMaleParts; + StringSet mDependencies; + }; + using RaceDataPtr = std::shared_ptr; - struct ActorData - { - ActorPartMap parts; - DependencyMap dependencies; - }; + /// Contains all the data needed to render an actor. Tracks dependencies + /// so that pertinent data changes can be checked. + class ActorData + { + public: + /// Retrieves the id of the actor represented + const std::string& getId() const; + /// Checks if the actor is female + bool isFemale() const; + /// Retrieves the associated actor part + const std::string& getPart(ESM::PartReferenceType index) const; + /// Checks if the actor has a data dependency + bool hasDependency(const std::string& id) const; - struct RaceData - { - RacePartMap femaleParts; - RacePartMap maleParts; - DependencyMap dependencies; - }; + /// Sets the actor part used and marks a dependency + void setPart(ESM::PartReferenceType partIndex, const std::string& partId); + /// Marks an additional dependency for the actor + void addOtherDependency(const std::string& id); + /// Clears race, parts, and dependencies + void reset(const std::string& actorId, bool female=true, RaceDataPtr raceData=nullptr); - ActorAdapter(const ActorAdapter&) = delete; - ActorAdapter& operator=(const ActorAdapter&) = delete; + private: + std::string mId; + bool mFemale; + RaceDataPtr mRaceData; + ActorPartList mParts; + StringSet mDependencies; + }; + using ActorDataPtr = std::shared_ptr; - QModelIndex getHighestIndex(QModelIndex) const; - bool is1stPersonPart(const std::string& id) const; - RaceData& getRaceData(const std::string& raceId); + ActorAdapter(Data& data); - void updateRace(const std::string& raceId); - void updateActor(const std::string& refId); - void updateNpc(const std::string& refId); - void updateCreature(const std::string& refId); + /// Obtains the shared data for a given actor + ActorDataPtr getActorData(const std::string& refId); - void updateActorsWithDependency(const std::string& id); - void updateRacesWithDependency(const std::string& id); + signals: - RefIdCollection& mReferenceables; - IdCollection& mRaces; - IdCollection& mBodyParts; + void actorChanged(const std::string& refId); - // Key: referenceable id - std::unordered_map mCachedActors; - // Key: race id - std::unordered_map mCachedRaces; + public slots: + + void handleReferenceablesInserted(const QModelIndex&, int, int); + void handleReferenceableChanged(const QModelIndex&, const QModelIndex&); + void handleReferenceablesAboutToBeRemoved(const QModelIndex&, int, int); + void handleReferenceablesRemoved(const QModelIndex&, int, int); + + void handleRacesInserted(const QModelIndex&, int, int); + void handleRaceChanged(const QModelIndex&, const QModelIndex&); + void handleRacesAboutToBeRemoved(const QModelIndex&, int, int); + void handleRacesRemoved(const QModelIndex&, int, int); + + void handleBodyPartsInserted(const QModelIndex&, int, int); + void handleBodyPartChanged(const QModelIndex&, const QModelIndex&); + void handleBodyPartsAboutToBeRemoved(const QModelIndex&, int, int); + void handleBodyPartsRemoved(const QModelIndex&, int, int); + + private: + + ActorAdapter(const ActorAdapter&) = delete; + ActorAdapter& operator=(const ActorAdapter&) = delete; + + QModelIndex getHighestIndex(QModelIndex) const; + bool is1stPersonPart(const std::string& id) const; + + RaceDataPtr getRaceData(const std::string& raceId); + + void setupActor(const std::string& id, ActorDataPtr data); + void setupRace(const std::string& id, RaceDataPtr data); + + void setupNpc(const std::string& id, ActorDataPtr data); + void addNpcItem(const std::string& itemId, ActorDataPtr data); + + void setupCreature(const std::string& id, ActorDataPtr data); + + void markDirtyDependency(const std::string& dependency); + void updateDirty(); + + RefIdCollection& mReferenceables; + IdCollection& mRaces; + IdCollection& mBodyParts; + + cache::WeakCache mCachedActors; // Key: referenceable id + cache::WeakCache mCachedRaces; // Key: race id + + StringSet mDirtyActors; // Actors that need updating + StringSet mDirtyRaces; // Races that need updating }; } diff --git a/apps/opencs/view/render/actor.cpp b/apps/opencs/view/render/actor.cpp index b9979ae2c..0a2519d0d 100644 --- a/apps/opencs/view/render/actor.cpp +++ b/apps/opencs/view/render/actor.cpp @@ -26,6 +26,7 @@ namespace CSVRender , mBaseNode(new osg::Group()) , mSkeleton(nullptr) { + mActorData = mData.getActorAdapter()->getActorData(mId); } osg::Group* Actor::getBaseNode() @@ -60,7 +61,6 @@ namespace CSVRender { if (mId == refId) { - Log(Debug::Info) << "Actor::actorChanged " << mId; update(); } } @@ -80,9 +80,6 @@ namespace CSVRender mSkeleton->accept(removeTriBipVisitor); removeTriBipVisitor.remove(); - // Attach weapons - loadBodyParts(creature.mId); - // Post setup mSkeleton->markDirty(); mSkeleton->setActive(SceneUtil::Skeleton::Active); @@ -141,12 +138,11 @@ namespace CSVRender void Actor::loadBodyParts(const std::string& actorId) { - auto actorAdapter = mData.getActorAdapter(); - auto parts = actorAdapter->getActorParts(actorId); - if (parts) + for (int i = 0; i < ESM::PRT_Count; ++i) { - for (auto& pair : *parts) - attachBodyPart(pair.first, getBodyPartMesh(pair.second)); + auto type = (ESM::PartReferenceType) i; + std::string partId = mActorData->getPart(type); + attachBodyPart(type, getBodyPartMesh(partId)); } } diff --git a/apps/opencs/view/render/actor.hpp b/apps/opencs/view/render/actor.hpp index 42a6019ed..d7cbb3067 100644 --- a/apps/opencs/view/render/actor.hpp +++ b/apps/opencs/view/render/actor.hpp @@ -10,6 +10,8 @@ #include #include +#include "../../model/world/actoradapter.hpp" + namespace osg { class Group; @@ -63,6 +65,7 @@ namespace CSVRender bool mInitialized; int mType; CSMWorld::Data& mData; + CSMWorld::ActorAdapter::ActorDataPtr mActorData; osg::ref_ptr mBaseNode; SceneUtil::Skeleton* mSkeleton; diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 7af76137c..494e1c5ce 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -150,6 +150,10 @@ add_component_dir (fallback fallback validate ) +add_component_dir(cache + weakcache + ) + if(NOT WIN32 AND NOT ANDROID) add_component_dir (crashcatcher crashcatcher diff --git a/components/cache/weakcache.hpp b/components/cache/weakcache.hpp new file mode 100644 index 000000000..9d940cd74 --- /dev/null +++ b/components/cache/weakcache.hpp @@ -0,0 +1,116 @@ +#ifndef OPENMW_COMPONENTS_WEAKCACHE_HPP +#define OPENMW_COMPONENTS_WEAKCACHE_HPP + +#include +#include + +namespace cache +{ + /// \class WeakCache + /// Provides a container to weakly store pointers to shared data. + template + class WeakCache + { + public: + using WeakPtr = std::weak_ptr; + using StrongPtr = std::shared_ptr; + using Map = std::unordered_map; + + class iterator + { + public: + iterator(typename Map::iterator current, typename Map::iterator end); + iterator& operator++(); + bool operator==(const iterator& other); + bool operator!=(const iterator& other); + StrongPtr operator*(); + private: + typename Map::iterator mCurrent, mEnd; + StrongPtr mPtr; + }; + + /// Stores a weak pointer to the item. + void insert(Key key, StrongPtr value); + + /// Retrieves the item associated with the key. + /// \return An item or null. + StrongPtr get(Key key); + + iterator begin(); + iterator end(); + + private: + Map mData; + }; + + + template + WeakCache::iterator::iterator(typename Map::iterator current, typename Map::iterator end) + : mCurrent(current) + , mEnd(end) + { + // Move to 1st available valid item + for ( ; mCurrent != mEnd; ++mCurrent) + { + mPtr = mCurrent->second.lock(); + if (mPtr) break; + } + } + + template + typename WeakCache::iterator& WeakCache::iterator::operator++() + { + auto next = mCurrent; + ++next; + return *this = iterator(next, mEnd); + } + + template + bool WeakCache::iterator::operator==(const iterator& other) + { + return mCurrent == other.mCurrent; + } + + template + bool WeakCache::iterator::operator!=(const iterator& other) + { + return !(*this == other); + } + + template + typename WeakCache::StrongPtr WeakCache::iterator::operator*() + { + return mPtr; + } + + + template + void WeakCache::insert(Key key, StrongPtr value) + { + mData[key] = WeakPtr(value); + } + + template + typename WeakCache::StrongPtr WeakCache::get(Key key) + { + auto searchIt = mData.find(key); + if (searchIt != mData.end()) + return searchIt->second.lock(); + else + return StrongPtr(); + } + + template + typename WeakCache::iterator WeakCache::begin() + { + return iterator(mData.begin(), mData.end()); + } + + template + typename WeakCache::iterator WeakCache::end() + { + return iterator(mData.end(), mData.end()); + } +} + +#endif From 1518d630caa7068e2cf405120a65680bd08ac3e5 Mon Sep 17 00:00:00 2001 From: Kyle Cooley Date: Fri, 7 Sep 2018 22:00:02 -0400 Subject: [PATCH 090/163] Fix issue with body part events not propogating to actors --- apps/opencs/model/world/actoradapter.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/apps/opencs/model/world/actoradapter.cpp b/apps/opencs/model/world/actoradapter.cpp index b3a1216f7..6f564bac5 100644 --- a/apps/opencs/model/world/actoradapter.cpp +++ b/apps/opencs/model/world/actoradapter.cpp @@ -555,6 +555,14 @@ namespace CSMWorld if (data) { setupRace(race, data); + // Race was changed. Need to mark actor dependencies as dirty. + // Cannot use markDirtyDependency because that would invalidate + // the current iterator. + for (auto actorIt : mCachedActors) + { + if (actorIt->hasDependency(race)) + mDirtyActors.emplace(actorIt->getId()); + } } } mDirtyRaces.clear(); From c1ec926f4327c46dc98003ff90ecaf9205c665bf Mon Sep 17 00:00:00 2001 From: Kyle Cooley Date: Fri, 7 Sep 2018 23:38:11 -0400 Subject: [PATCH 091/163] Workaround inconsistencies with record status changes --- apps/opencs/model/world/actoradapter.cpp | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/apps/opencs/model/world/actoradapter.cpp b/apps/opencs/model/world/actoradapter.cpp index 6f564bac5..d081f8a3e 100644 --- a/apps/opencs/model/world/actoradapter.cpp +++ b/apps/opencs/model/world/actoradapter.cpp @@ -195,6 +195,10 @@ namespace CSMWorld int start = getHighestIndex(topLeft).row(); int end = getHighestIndex(botRight).row(); + // A change to record status (ex. Deleted) returns an invalid botRight + if (end == -1) + end = start; + // Handle each record for (int row = start; row <= end; ++row) { @@ -245,6 +249,11 @@ namespace CSMWorld { int start = getHighestIndex(topLeft).row(); int end = getHighestIndex(botRight).row(); + + // A change to record status (ex. Deleted) returns an invalid botRight + if (end == -1) + end = start; + for (int row = start; row <= end; ++row) { std::string raceId = mRaces.getId(row); @@ -299,9 +308,14 @@ namespace CSMWorld void ActorAdapter::handleBodyPartChanged(const QModelIndex& topLeft, const QModelIndex& botRight) { - int rowStart = getHighestIndex(topLeft).row(); - int rowEnd = getHighestIndex(botRight).row(); - for (int row = rowStart; row <= rowEnd; ++row) + int start = getHighestIndex(topLeft).row(); + int end = getHighestIndex(botRight).row(); + + // A change to record status (ex. Deleted) returns an invalid botRight + if (end == -1) + end = start; + + for (int row = start; row <= end; ++row) { // Race specified by part may need update auto& record = mBodyParts.getRecord(row); From f43b70d77be6a21015c93db5076edcc118594c53 Mon Sep 17 00:00:00 2001 From: Kyle Cooley Date: Sat, 8 Sep 2018 02:06:48 -0400 Subject: [PATCH 092/163] Centralize actor data, simplify logic --- apps/opencs/model/world/actoradapter.cpp | 52 ++++++++++--- apps/opencs/model/world/actoradapter.hpp | 13 +++- apps/opencs/view/render/actor.cpp | 97 +++++++----------------- apps/opencs/view/render/actor.hpp | 9 +-- apps/opencs/view/render/object.cpp | 88 +++++++++------------ 5 files changed, 116 insertions(+), 143 deletions(-) diff --git a/apps/opencs/model/world/actoradapter.cpp b/apps/opencs/model/world/actoradapter.cpp index d081f8a3e..47bc5f4da 100644 --- a/apps/opencs/model/world/actoradapter.cpp +++ b/apps/opencs/model/world/actoradapter.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include "data.hpp" @@ -16,6 +17,11 @@ namespace CSMWorld return mId; } + bool ActorAdapter::RaceData::isBeast() const + { + return mIsBeast; + } + bool ActorAdapter::RaceData::handlesPart(ESM::PartReferenceType type) const { switch (type) @@ -63,9 +69,10 @@ namespace CSMWorld if (!id.empty()) mDependencies.emplace(id); } - void ActorAdapter::RaceData::reset(const std::string& id) + void ActorAdapter::RaceData::reset_data(const std::string& id, bool isBeast) { mId = id; + mIsBeast = isBeast; for (auto& str : mFemaleParts) str.clear(); for (auto& str : mMaleParts) @@ -82,11 +89,28 @@ namespace CSMWorld return mId; } + bool ActorAdapter::ActorData::isCreature() const + { + return mCreature; + } + bool ActorAdapter::ActorData::isFemale() const { return mFemale; } + std::string ActorAdapter::ActorData::getSkeleton() const + { + if (mCreature || !mSkeletonOverride.empty()) + return "meshes\\" + mSkeletonOverride; + + bool firstPerson = false; + bool beast = mRaceData ? mRaceData->isBeast() : false; + bool werewolf = false; + + return SceneUtil::getActorSkeleton(firstPerson, mFemale, beast, werewolf); + } + const std::string& ActorAdapter::ActorData::getPart(ESM::PartReferenceType index) const { if (mParts[index].empty() && mRaceData && mRaceData->handlesPart(index)) @@ -112,10 +136,12 @@ namespace CSMWorld if (!id.empty()) mDependencies.emplace(id); } - void ActorAdapter::ActorData::reset(const std::string& id, bool isFemale, RaceDataPtr raceData) + void ActorAdapter::ActorData::reset_data(const std::string& id, const std::string& skeleton, bool isCreature, bool isFemale, RaceDataPtr raceData) { mId = id; + mCreature = isCreature; mFemale = isFemale; + mSkeletonOverride = skeleton; mRaceData = raceData; for (auto& str : mParts) str.clear(); @@ -383,7 +409,7 @@ namespace CSMWorld if (index == -1) { // Record does not exist - data->reset(id); + data->reset_data(id); emit actorChanged(id); return; } @@ -392,7 +418,7 @@ namespace CSMWorld if (record.isDeleted()) { // Record is deleted and therefore not accessible - data->reset(id); + data->reset_data(id); emit actorChanged(id); return; } @@ -414,20 +440,18 @@ namespace CSMWorld else { // Wrong record type - data->reset(id); + data->reset_data(id); emit actorChanged(id); } } void ActorAdapter::setupRace(const std::string& id, RaceDataPtr data) { - // Common setup - data->reset(id); - int index = mRaces.searchId(id); if (index == -1) { // Record does not exist + data->reset_data(id); return; } @@ -435,10 +459,12 @@ namespace CSMWorld if (raceRecord.isDeleted()) { // Record is deleted, so not accessible + data->reset_data(id); return; } - // TODO move stuff in actor related to race here + auto& race = raceRecord.get(); + data->reset_data(id, race.mData.mFlags & ESM::Race::Beast); // Setup body parts for (int i = 0; i < mBodyParts.getSize(); ++i) @@ -470,7 +496,7 @@ namespace CSMWorld auto& npc = dynamic_cast&>(mReferenceables.getRecord(index)).get(); RaceDataPtr raceData = getRaceData(npc.mRace); - data->reset(id, !npc.isMale(), raceData); + data->reset_data(id, "", false, !npc.isMale(), raceData); // Add inventory items for (auto& item : npc.mInventory.mList) @@ -541,9 +567,11 @@ namespace CSMWorld void ActorAdapter::setupCreature(const std::string& id, ActorDataPtr data) { - data->reset(id); + // Record is known to exist and is not deleted + int index = mReferenceables.searchId(id); + auto& creature = dynamic_cast&>(mReferenceables.getRecord(index)).get(); - // TODO move stuff from Actor here + data->reset_data(id, creature.mModel, true); } void ActorAdapter::markDirtyDependency(const std::string& dep) diff --git a/apps/opencs/model/world/actoradapter.hpp b/apps/opencs/model/world/actoradapter.hpp index 0e8b0c9fe..ba714e840 100644 --- a/apps/opencs/model/world/actoradapter.hpp +++ b/apps/opencs/model/world/actoradapter.hpp @@ -44,6 +44,8 @@ namespace CSMWorld public: /// Retrieves the id of the race represented const std::string& getId() const; + /// Checks if it's a beast race + bool isBeast() const; /// Checks if a part could exist for the given type bool handlesPart(ESM::PartReferenceType type) const; /// Retrieves the associated body part @@ -60,11 +62,12 @@ namespace CSMWorld /// Marks an additional dependency void addOtherDependency(const std::string& id); /// Clears parts and dependencies - void reset(const std::string& raceId); + void reset_data(const std::string& raceId, bool isBeast=false); private: bool handles(ESM::PartReferenceType type) const; std::string mId; + bool mIsBeast; RacePartList mFemaleParts; RacePartList mMaleParts; StringSet mDependencies; @@ -78,8 +81,12 @@ namespace CSMWorld public: /// Retrieves the id of the actor represented const std::string& getId() const; + /// Checks if the actor is a creature + bool isCreature() const; /// Checks if the actor is female bool isFemale() const; + /// Returns the skeleton the actor should use for attaching parts to + std::string getSkeleton() const; /// Retrieves the associated actor part const std::string& getPart(ESM::PartReferenceType index) const; /// Checks if the actor has a data dependency @@ -90,11 +97,13 @@ namespace CSMWorld /// Marks an additional dependency for the actor void addOtherDependency(const std::string& id); /// Clears race, parts, and dependencies - void reset(const std::string& actorId, bool female=true, RaceDataPtr raceData=nullptr); + void reset_data(const std::string& actorId, const std::string& skeleton="", bool isCreature=false, bool female=true, RaceDataPtr raceData=nullptr); private: std::string mId; + bool mCreature; bool mFemale; + std::string mSkeletonOverride; RaceDataPtr mRaceData; ActorPartList mParts; StringSet mDependencies; diff --git a/apps/opencs/view/render/actor.cpp b/apps/opencs/view/render/actor.cpp index 0a2519d0d..7238166fc 100644 --- a/apps/opencs/view/render/actor.cpp +++ b/apps/opencs/view/render/actor.cpp @@ -18,15 +18,15 @@ namespace CSVRender { const std::string Actor::MeshPrefix = "meshes\\"; - Actor::Actor(const std::string& id, int type, CSMWorld::Data& data) + Actor::Actor(const std::string& id, CSMWorld::Data& data) : mId(id) - , mInitialized(false) - , mType(type) , mData(data) , mBaseNode(new osg::Group()) , mSkeleton(nullptr) { mActorData = mData.getActorAdapter()->getActorData(mId); + connect(mData.getActorAdapter(), SIGNAL(actorChanged(const std::string&)), + this, SLOT(handleActorChanged(const std::string&))); } osg::Group* Actor::getBaseNode() @@ -36,25 +36,33 @@ namespace CSVRender void Actor::update() { - try - { - mBaseNode->removeChildren(0, mBaseNode->getNumChildren()); + mBaseNode->removeChildren(0, mBaseNode->getNumChildren()); - if (mType == CSMWorld::UniversalId::Type_Npc) - updateNpc(); - else if (mType == CSMWorld::UniversalId::Type_Creature) - updateCreature(); - } - catch (std::exception& e) + // Load skeleton + std::string skeletonModel = mActorData->getSkeleton(); + skeletonModel = Misc::ResourceHelpers::correctActorModelPath(skeletonModel, mData.getResourceSystem()->getVFS()); + loadSkeleton(skeletonModel); + + if (!mActorData->isCreature()) { - Log(Debug::Info) << "Exception in Actor::update(): " << e.what(); + // Get rid of the extra attachments + SceneUtil::CleanObjectRootVisitor cleanVisitor; + mSkeleton->accept(cleanVisitor); + cleanVisitor.remove(); + + // Attach parts to skeleton + loadBodyParts(); + } + else + { + SceneUtil::RemoveTriBipVisitor removeTriBipVisitor; + mSkeleton->accept(removeTriBipVisitor); + removeTriBipVisitor.remove(); } - if (!mInitialized) - { - mInitialized = true; - connect(mData.getActorAdapter(), SIGNAL(actorChanged(const std::string&)), this, SLOT(handleActorChanged(const std::string&))); - } + // Post setup + mSkeleton->markDirty(); + mSkeleton->setActive(SceneUtil::Skeleton::Active); } void Actor::handleActorChanged(const std::string& refId) @@ -65,57 +73,6 @@ namespace CSVRender } } - void Actor::updateCreature() - { - auto& referenceables = mData.getReferenceables(); - - auto& creature = dynamic_cast& >(referenceables.getRecord(mId)).get(); - - // Load skeleton with meshes - std::string skeletonModel = MeshPrefix + creature.mModel; - skeletonModel = Misc::ResourceHelpers::correctActorModelPath(skeletonModel, mData.getResourceSystem()->getVFS()); - loadSkeleton(skeletonModel); - - SceneUtil::RemoveTriBipVisitor removeTriBipVisitor; - mSkeleton->accept(removeTriBipVisitor); - removeTriBipVisitor.remove(); - - // Post setup - mSkeleton->markDirty(); - mSkeleton->setActive(SceneUtil::Skeleton::Active); - } - - void Actor::updateNpc() - { - auto& races = mData.getRaces(); - auto& referenceables = mData.getReferenceables(); - - auto& npc = dynamic_cast& >(referenceables.getRecord(mId)).get(); - auto& race = dynamic_cast& >(races.getRecord(npc.mRace)).get(); - - bool is1stPerson = false; - bool isFemale = !npc.isMale(); - bool isBeast = race.mData.mFlags & ESM::Race::Beast; - bool isWerewolf = false; - - // Load skeleton - std::string skeletonModel = SceneUtil::getActorSkeleton(is1stPerson, isFemale, isBeast, isWerewolf); - skeletonModel = Misc::ResourceHelpers::correctActorModelPath(skeletonModel, mData.getResourceSystem()->getVFS()); - loadSkeleton(skeletonModel); - - // Get rid of the extra attachments - SceneUtil::CleanObjectRootVisitor cleanVisitor; - mSkeleton->accept(cleanVisitor); - cleanVisitor.remove(); - - // Attach parts to skeleton - loadBodyParts(npc.mId); - - // Post setup - mSkeleton->markDirty(); - mSkeleton->setActive(SceneUtil::Skeleton::Active); - } - void Actor::loadSkeleton(const std::string& model) { auto sceneMgr = mData.getResourceSystem()->getSceneManager(); @@ -136,7 +93,7 @@ namespace CSVRender } - void Actor::loadBodyParts(const std::string& actorId) + void Actor::loadBodyParts() { for (int i = 0; i < ESM::PRT_Count; ++i) { diff --git a/apps/opencs/view/render/actor.hpp b/apps/opencs/view/render/actor.hpp index d7cbb3067..2f19454f7 100644 --- a/apps/opencs/view/render/actor.hpp +++ b/apps/opencs/view/render/actor.hpp @@ -38,7 +38,7 @@ namespace CSVRender /// \param id The referenceable id /// \param type The record type /// \param data The data store - Actor(const std::string& id, int type, CSMWorld::Data& data); + Actor(const std::string& id, CSMWorld::Data& data); /// Retrieves the base node that meshes are attached to osg::Group* getBaseNode(); @@ -50,11 +50,8 @@ namespace CSVRender void handleActorChanged(const std::string& refId); private: - void updateCreature(); - void updateNpc(); - void loadSkeleton(const std::string& model); - void loadBodyParts(const std::string& actorId); + void loadBodyParts(); void attachBodyPart(ESM::PartReferenceType, const std::string& mesh); std::string getBodyPartMesh(const std::string& bodyPartId); @@ -62,8 +59,6 @@ namespace CSVRender static const std::string MeshPrefix; std::string mId; - bool mInitialized; - int mType; CSMWorld::Data& mData; CSMWorld::ActorAdapter::ActorDataPtr mActorData; diff --git a/apps/opencs/view/render/object.cpp b/apps/opencs/view/render/object.cpp index 63f1bc8a8..6f0fb6606 100644 --- a/apps/opencs/view/render/object.cpp +++ b/apps/opencs/view/render/object.cpp @@ -83,74 +83,58 @@ void CSVRender::Object::update() { clear(); - std::string model; - int error = 0; // 1 referenceable does not exist, 2 referenceable does not specify a mesh - const CSMWorld::RefIdCollection& referenceables = mData.getReferenceables(); + const int TypeIndex = referenceables.findColumnIndex(CSMWorld::Columns::ColumnId_RecordType); + const int ModelIndex = referenceables.findColumnIndex (CSMWorld::Columns::ColumnId_Model); int index = referenceables.searchId (mReferenceableId); - int recordType = -1; const ESM::Light* light = NULL; - if (index==-1) - error = 1; - else - { - /// \todo check for Deleted state (error 1) - - model = referenceables.getData (index, - referenceables.findColumnIndex (CSMWorld::Columns::ColumnId_Model)). - toString().toUtf8().constData(); - - recordType = - referenceables.getData (index, - referenceables.findColumnIndex(CSMWorld::Columns::ColumnId_RecordType)).toInt(); - if (recordType == CSMWorld::UniversalId::Type_Light) - { - light = &dynamic_cast& >(referenceables.getRecord(index)).get(); - if (model.empty()) - model = "marker_light.nif"; - } - - if (recordType == CSMWorld::UniversalId::Type_CreatureLevelledList) - { - if (model.empty()) - model = "marker_creature.nif"; - } - - if (recordType != CSMWorld::UniversalId::Type_Npc && model.empty()) - error = 2; - } - mBaseNode->removeChildren(0, mBaseNode->getNumChildren()); - if (error) + if (index == -1) { mBaseNode->addChild(createErrorCube()); + return; } - else + + /// \todo check for Deleted state (error 1) + + int recordType = referenceables.getData(index, TypeIndex).toInt(); + std::string model = referenceables.getData(index, ModelIndex).toString().toUtf8().constData(); + + if (recordType == CSMWorld::UniversalId::Type_Light) { - try + light = &dynamic_cast& >(referenceables.getRecord(index)).get(); + if (model.empty()) + model = "marker_light.nif"; + } + + if (recordType == CSMWorld::UniversalId::Type_CreatureLevelledList) + { + if (model.empty()) + model = "marker_creature.nif"; + } + + try + { + if (recordType == CSMWorld::UniversalId::Type_Npc || recordType == CSMWorld::UniversalId::Type_Creature) { - if (recordType == CSMWorld::UniversalId::Type_Npc || recordType == CSMWorld::UniversalId::Type_Creature) - { - if (!mActor) - mActor.reset(new Actor(mReferenceableId, recordType, mData)); - mActor->update(); - mBaseNode->addChild(mActor->getBaseNode()); - } - else - { - std::string path = "meshes\\" + model; - mResourceSystem->getSceneManager()->getInstance(path, mBaseNode); - } + if (!mActor) mActor.reset(new Actor(mReferenceableId, mData)); + mActor->update(); + mBaseNode->addChild(mActor->getBaseNode()); } - catch (std::exception& e) + else { - Log(Debug::Error) << e.what(); - mBaseNode->addChild(createErrorCube()); + std::string path = "meshes\\" + model; + mResourceSystem->getSceneManager()->getInstance(path, mBaseNode); } } + catch (std::exception& e) + { + // TODO: use error marker mesh + Log(Debug::Error) << e.what(); + } if (light) { From 676fc488557453f1b49c2d0441221ac573de2efb Mon Sep 17 00:00:00 2001 From: Kyle Cooley Date: Sat, 8 Sep 2018 02:24:33 -0400 Subject: [PATCH 093/163] Re-add logic for empty model --- apps/opencs/view/render/object.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/opencs/view/render/object.cpp b/apps/opencs/view/render/object.cpp index 6f0fb6606..8b3b5ca25 100644 --- a/apps/opencs/view/render/object.cpp +++ b/apps/opencs/view/render/object.cpp @@ -124,11 +124,15 @@ void CSVRender::Object::update() mActor->update(); mBaseNode->addChild(mActor->getBaseNode()); } - else + else if (!model.empty()) { std::string path = "meshes\\" + model; mResourceSystem->getSceneManager()->getInstance(path, mBaseNode); } + else + { + throw std::runtime_error(mReferenceableId + " has no model"); + } } catch (std::exception& e) { From 0096951f2598df434e436000c994c1a3320179d7 Mon Sep 17 00:00:00 2001 From: Kyle Cooley Date: Sat, 8 Sep 2018 02:32:47 -0400 Subject: [PATCH 094/163] cleanup --- apps/opencs/model/world/actoradapter.cpp | 1 - apps/opencs/model/world/data.hpp | 1 - apps/opencs/view/render/actor.cpp | 2 -- apps/opencs/view/render/object.cpp | 2 -- 4 files changed, 6 deletions(-) diff --git a/apps/opencs/model/world/actoradapter.cpp b/apps/opencs/model/world/actoradapter.cpp index 47bc5f4da..8c9e5bd1f 100644 --- a/apps/opencs/model/world/actoradapter.cpp +++ b/apps/opencs/model/world/actoradapter.cpp @@ -1,6 +1,5 @@ #include "actoradapter.hpp" -#include #include #include #include diff --git a/apps/opencs/model/world/data.hpp b/apps/opencs/model/world/data.hpp index 7c4d8885a..e50780f50 100644 --- a/apps/opencs/model/world/data.hpp +++ b/apps/opencs/model/world/data.hpp @@ -74,7 +74,6 @@ namespace ESM namespace CSMWorld { - class ActorAdapter; class ResourcesManager; class Resources; diff --git a/apps/opencs/view/render/actor.cpp b/apps/opencs/view/render/actor.cpp index 7238166fc..d6077a65a 100644 --- a/apps/opencs/view/render/actor.cpp +++ b/apps/opencs/view/render/actor.cpp @@ -3,12 +3,10 @@ #include #include -#include #include #include #include #include -#include #include #include diff --git a/apps/opencs/view/render/object.cpp b/apps/opencs/view/render/object.cpp index 8b3b5ca25..36d80fa4b 100644 --- a/apps/opencs/view/render/object.cpp +++ b/apps/opencs/view/render/object.cpp @@ -13,7 +13,6 @@ #include #include #include -#include #include @@ -31,7 +30,6 @@ #include #include #include -#include #include "actor.hpp" #include "mask.hpp" From 7eb1b14b21ba0122b1aac63c1b5a2be38249c6f7 Mon Sep 17 00:00:00 2001 From: Kyle Cooley Date: Sat, 8 Sep 2018 03:13:03 -0400 Subject: [PATCH 095/163] Periodically prune empty elements in weak cache --- components/cache/weakcache.hpp | 38 +++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/components/cache/weakcache.hpp b/components/cache/weakcache.hpp index 9d940cd74..976818cc2 100644 --- a/components/cache/weakcache.hpp +++ b/components/cache/weakcache.hpp @@ -3,6 +3,7 @@ #include #include +#include namespace cache { @@ -19,18 +20,19 @@ namespace cache class iterator { public: - iterator(typename Map::iterator current, typename Map::iterator end); + iterator(WeakCache* cache, typename Map::iterator current, typename Map::iterator end); iterator& operator++(); bool operator==(const iterator& other); bool operator!=(const iterator& other); StrongPtr operator*(); private: + WeakCache* mCache; typename Map::iterator mCurrent, mEnd; StrongPtr mPtr; }; /// Stores a weak pointer to the item. - void insert(Key key, StrongPtr value); + void insert(Key key, StrongPtr value, bool prune=true); /// Retrieves the item associated with the key. /// \return An item or null. @@ -39,14 +41,19 @@ namespace cache iterator begin(); iterator end(); + /// Removes known invalid entries + void prune(); + private: Map mData; + std::vector mDirty; }; template - WeakCache::iterator::iterator(typename Map::iterator current, typename Map::iterator end) - : mCurrent(current) + WeakCache::iterator::iterator(WeakCache* cache, typename Map::iterator current, typename Map::iterator end) + : mCache(cache) + , mCurrent(current) , mEnd(end) { // Move to 1st available valid item @@ -54,6 +61,7 @@ namespace cache { mPtr = mCurrent->second.lock(); if (mPtr) break; + else mCache->mDirty.push_back(mCurrent->first); } } @@ -62,7 +70,7 @@ namespace cache { auto next = mCurrent; ++next; - return *this = iterator(next, mEnd); + return *this = iterator(mCache, next, mEnd); } template @@ -85,9 +93,10 @@ namespace cache template - void WeakCache::insert(Key key, StrongPtr value) + void WeakCache::insert(Key key, StrongPtr value, bool shouldPrune) { mData[key] = WeakPtr(value); + if (shouldPrune) prune(); } template @@ -103,13 +112,26 @@ namespace cache template typename WeakCache::iterator WeakCache::begin() { - return iterator(mData.begin(), mData.end()); + return iterator(this, mData.begin(), mData.end()); } template typename WeakCache::iterator WeakCache::end() { - return iterator(mData.end(), mData.end()); + return iterator(this, mData.end(), mData.end()); + } + + template + void WeakCache::prune() + { + // Remove empty entries + for (auto& key : mDirty) + { + auto it = mData.find(key); + if (it != mData.end() && it->second.use_count() == 0) + mData.erase(it); + } + mDirty.clear(); } } From ac848b09028315850916bf644a22c965bd98581a Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Thu, 27 Sep 2018 13:14:34 +0400 Subject: [PATCH 096/163] Use male bodyparts as fallback for females in the editor --- apps/opencs/model/world/actoradapter.cpp | 12 ++++++++++-- apps/opencs/model/world/actoradapter.hpp | 2 +- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/apps/opencs/model/world/actoradapter.cpp b/apps/opencs/model/world/actoradapter.cpp index 8c9e5bd1f..425c4c804 100644 --- a/apps/opencs/model/world/actoradapter.cpp +++ b/apps/opencs/model/world/actoradapter.cpp @@ -110,11 +110,19 @@ namespace CSMWorld return SceneUtil::getActorSkeleton(firstPerson, mFemale, beast, werewolf); } - const std::string& ActorAdapter::ActorData::getPart(ESM::PartReferenceType index) const + const std::string ActorAdapter::ActorData::getPart(ESM::PartReferenceType index) const { if (mParts[index].empty() && mRaceData && mRaceData->handlesPart(index)) { - return mFemale ? mRaceData->getFemalePart(index) : mRaceData->getMalePart(index); + if (mFemale) + { + // Note: we should use male parts for females as fallback + const std::string femalePart = mRaceData->getFemalePart(index); + if (!femalePart.empty()) + return femalePart; + } + + return mRaceData->getMalePart(index); } return mParts[index]; } diff --git a/apps/opencs/model/world/actoradapter.hpp b/apps/opencs/model/world/actoradapter.hpp index ba714e840..d842bca35 100644 --- a/apps/opencs/model/world/actoradapter.hpp +++ b/apps/opencs/model/world/actoradapter.hpp @@ -88,7 +88,7 @@ namespace CSMWorld /// Returns the skeleton the actor should use for attaching parts to std::string getSkeleton() const; /// Retrieves the associated actor part - const std::string& getPart(ESM::PartReferenceType index) const; + const std::string getPart(ESM::PartReferenceType index) const; /// Checks if the actor has a data dependency bool hasDependency(const std::string& id) const; From 35abf7367cf85220c0117c270906661f73e8af76 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Thu, 27 Sep 2018 18:22:17 +0400 Subject: [PATCH 097/163] Implement wearing priority for editor --- CHANGELOG.md | 1 + apps/opencs/model/world/actoradapter.cpp | 83 ++++++++++++++++++++---- apps/opencs/model/world/actoradapter.hpp | 5 +- 3 files changed, 74 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 04547e249..c72ac7b9e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -132,6 +132,7 @@ Bug #4653: Length of non-ASCII strings is handled incorrectly in ESM reader Bug #4654: Editor: UpdateVisitor does not initialize skeletons for animated objects Feature #912: Editor: Add missing icons to UniversalId tables + Feature #1221: Editor: Creature/NPC rendering Feature #1617: Editor: Enchantment effect record verifier Feature #1645: Casting effects from objects Feature #2606: Editor: Implemented (optional) case sensitive global search diff --git a/apps/opencs/model/world/actoradapter.cpp b/apps/opencs/model/world/actoradapter.cpp index 425c4c804..ea40a1f8d 100644 --- a/apps/opencs/model/world/actoradapter.cpp +++ b/apps/opencs/model/world/actoradapter.cpp @@ -112,7 +112,8 @@ namespace CSMWorld const std::string ActorAdapter::ActorData::getPart(ESM::PartReferenceType index) const { - if (mParts[index].empty() && mRaceData && mRaceData->handlesPart(index)) + auto it = mParts.find(index); + if (it == mParts.end() && mRaceData && mRaceData->handlesPart(index)) { if (mFemale) { @@ -124,7 +125,9 @@ namespace CSMWorld return mRaceData->getMalePart(index); } - return mParts[index]; + + const std::string& partName = it->second.first; + return partName; } bool ActorAdapter::ActorData::hasDependency(const std::string& id) const @@ -132,9 +135,16 @@ namespace CSMWorld return mDependencies.find(id) != mDependencies.end(); } - void ActorAdapter::ActorData::setPart(ESM::PartReferenceType index, const std::string& partId) + void ActorAdapter::ActorData::setPart(ESM::PartReferenceType index, const std::string& partId, int priority) { - mParts[index] = partId; + auto it = mParts.find(index); + if (it != mParts.end()) + { + if (it->second.second >= priority) + return; + } + + mParts[index] = std::make_pair(partId, priority); addOtherDependency(partId); } @@ -150,8 +160,7 @@ namespace CSMWorld mFemale = isFemale; mSkeletonOverride = skeleton; mRaceData = raceData; - for (auto& str : mParts) - str.clear(); + mParts.clear(); mDependencies.clear(); // Mark self and race as a dependency @@ -514,8 +523,8 @@ namespace CSMWorld } // Add head and hair - data->setPart(ESM::PRT_Head, npc.mHead); - data->setPart(ESM::PRT_Hair, npc.mHair); + data->setPart(ESM::PRT_Head, npc.mHead, 0); + data->setPart(ESM::PRT_Hair, npc.mHair, 0); } void ActorAdapter::addNpcItem(const std::string& itemId, ActorDataPtr data) @@ -537,8 +546,8 @@ namespace CSMWorld } // Convenience function to add a parts list to actor data - auto addParts = [&](const ESM::PartReferenceList& list) { - for (auto& part : list.mParts) + auto addParts = [&](const std::vector& list, int priority) { + for (auto& part : list) { std::string partId; auto partType = (ESM::PartReferenceType) part.mPart; @@ -548,7 +557,7 @@ namespace CSMWorld if (partId.empty()) partId = part.mMale; - if (!partId.empty()) data->setPart(partType, partId); + data->setPart(partType, partId, priority); } }; @@ -557,15 +566,63 @@ namespace CSMWorld if (type == UniversalId::Type_Armor) { auto& armor = dynamic_cast&>(record).get(); - addParts(armor.mParts); + addParts(armor.mParts.mParts, 1); // Changing parts could affect what is picked for rendering data->addOtherDependency(itemId); } else if (type == UniversalId::Type_Clothing) { + int priority = 0; + // TODO: reserve bodyparts for robes and skirts auto& clothing = dynamic_cast&>(record).get(); - addParts(clothing.mParts); + + if (clothing.mData.mType == ESM::Clothing::Robe) + { + auto reservedList = std::vector(); + + ESM::PartReference pr; + pr.mMale = ""; + pr.mFemale = ""; + + ESM::PartReferenceType parts[] = { + ESM::PRT_Groin, ESM::PRT_Skirt, ESM::PRT_RLeg, ESM::PRT_LLeg, + ESM::PRT_RUpperarm, ESM::PRT_LUpperarm, ESM::PRT_RKnee, ESM::PRT_LKnee, + ESM::PRT_RForearm, ESM::PRT_LForearm + }; + size_t parts_size = sizeof(parts)/sizeof(parts[0]); + for(size_t p = 0;p < parts_size;++p) + { + pr.mPart = parts[p]; + reservedList.push_back(pr); + } + + priority = parts_size; + addParts(reservedList, priority); + } + else if (clothing.mData.mType == ESM::Clothing::Skirt) + { + auto reservedList = std::vector(); + + ESM::PartReference pr; + pr.mMale = ""; + pr.mFemale = ""; + + ESM::PartReferenceType parts[] = { + ESM::PRT_Groin, ESM::PRT_RLeg, ESM::PRT_LLeg + }; + size_t parts_size = sizeof(parts)/sizeof(parts[0]); + for(size_t p = 0;p < parts_size;++p) + { + pr.mPart = parts[p]; + reservedList.push_back(pr); + } + + priority = parts_size; + addParts(reservedList, priority); + } + + addParts(clothing.mParts.mParts, priority); // Changing parts could affect what is picked for rendering data->addOtherDependency(itemId); diff --git a/apps/opencs/model/world/actoradapter.hpp b/apps/opencs/model/world/actoradapter.hpp index d842bca35..b97c652a8 100644 --- a/apps/opencs/model/world/actoradapter.hpp +++ b/apps/opencs/model/world/actoradapter.hpp @@ -2,6 +2,7 @@ #define CSM_WOLRD_ACTORADAPTER_H #include +#include #include #include @@ -31,7 +32,7 @@ namespace CSMWorld public: /// A list indexed by ESM::PartReferenceType - using ActorPartList = std::array; + using ActorPartList = std::map>; /// A list indexed by ESM::BodyPart::MeshPart using RacePartList = std::array; /// Tracks unique strings @@ -93,7 +94,7 @@ namespace CSMWorld bool hasDependency(const std::string& id) const; /// Sets the actor part used and marks a dependency - void setPart(ESM::PartReferenceType partIndex, const std::string& partId); + void setPart(ESM::PartReferenceType partIndex, const std::string& partId, int priority); /// Marks an additional dependency for the actor void addOtherDependency(const std::string& id); /// Clears race, parts, and dependencies From 2e98cad895f48c30c03382a98f09b1d88af19ba7 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Thu, 27 Sep 2018 17:34:46 +0300 Subject: [PATCH 098/163] Fade out sun glare and specularity completely at night start, not sunset start --- apps/openmw/mwworld/weather.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index ac885f429..7d3f41894 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -751,14 +751,14 @@ void WeatherManager::update(float duration, bool paused, const TimeStamp& time, float underwaterFog = mUnderwaterFog.getValue(time.getHour(), mTimeSettings, "Fog"); - float peakHour = mSunriseTime + (mSunsetTime - mSunriseTime) / 2; + float peakHour = mSunriseTime + (mTimeSettings.mNightStart - mSunriseTime) / 2; float glareFade = 1.f; - if (time.getHour() < mSunriseTime || time.getHour() > mSunsetTime) + if (time.getHour() < mSunriseTime || time.getHour() > mTimeSettings.mNightStart) glareFade = 0.f; else if (time.getHour() < peakHour) - glareFade -= (peakHour - time.getHour()) / (peakHour - mSunriseTime); + glareFade = 1.f - (peakHour - time.getHour()) / (peakHour - mSunriseTime); else - glareFade -= (time.getHour() - peakHour) / (mSunsetTime - peakHour); + glareFade = 1.f - (time.getHour() - peakHour) / (mTimeSettings.mNightStart - peakHour); mRendering.getSkyManager()->setGlareTimeOfDayFade(glareFade); From 43c7438e8e9907cfc0e895f11a7176cd03837d61 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Fri, 28 Sep 2018 16:47:47 +0400 Subject: [PATCH 099/163] Move WeakCache to components/misc --- apps/opencs/model/world/actoradapter.hpp | 6 +++--- components/CMakeLists.txt | 6 +----- components/{cache => misc}/weakcache.hpp | 2 +- 3 files changed, 5 insertions(+), 9 deletions(-) rename components/{cache => misc}/weakcache.hpp (99%) diff --git a/apps/opencs/model/world/actoradapter.hpp b/apps/opencs/model/world/actoradapter.hpp index b97c652a8..1c9265be4 100644 --- a/apps/opencs/model/world/actoradapter.hpp +++ b/apps/opencs/model/world/actoradapter.hpp @@ -8,9 +8,9 @@ #include #include -#include #include #include +#include #include "refidcollection.hpp" #include "idcollection.hpp" @@ -163,8 +163,8 @@ namespace CSMWorld IdCollection& mRaces; IdCollection& mBodyParts; - cache::WeakCache mCachedActors; // Key: referenceable id - cache::WeakCache mCachedRaces; // Key: race id + Misc::WeakCache mCachedActors; // Key: referenceable id + Misc::WeakCache mCachedRaces; // Key: race id StringSet mDirtyActors; // Actors that need updating StringSet mDirtyRaces; // Races that need updating diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 494e1c5ce..5c245afd0 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -86,7 +86,7 @@ add_component_dir (esmterrain ) add_component_dir (misc - constants utf8stream stringops resourcehelpers rng messageformatparser + constants utf8stream stringops resourcehelpers rng messageformatparser weakcache ) add_component_dir (debug @@ -150,10 +150,6 @@ add_component_dir (fallback fallback validate ) -add_component_dir(cache - weakcache - ) - if(NOT WIN32 AND NOT ANDROID) add_component_dir (crashcatcher crashcatcher diff --git a/components/cache/weakcache.hpp b/components/misc/weakcache.hpp similarity index 99% rename from components/cache/weakcache.hpp rename to components/misc/weakcache.hpp index 976818cc2..022a722db 100644 --- a/components/cache/weakcache.hpp +++ b/components/misc/weakcache.hpp @@ -5,7 +5,7 @@ #include #include -namespace cache +namespace Misc { /// \class WeakCache /// Provides a container to weakly store pointers to shared data. From 2073218fc68e6a3799576aecdf624d76a4a1a284 Mon Sep 17 00:00:00 2001 From: elsid Date: Fri, 28 Sep 2018 22:28:54 +0300 Subject: [PATCH 100/163] Use specific googletest version --- CI/build_googletest.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CI/build_googletest.sh b/CI/build_googletest.sh index cd61379b5..ee89ebda6 100755 --- a/CI/build_googletest.sh +++ b/CI/build_googletest.sh @@ -1,6 +1,6 @@ #!/bin/sh -e -git clone https://github.com/google/googletest.git +git clone -b release-1.8.1 https://github.com/google/googletest.git cd googletest mkdir build cd build From f20f49daa6fb0f2c5ccc2f7ae5ad141e914ba332 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 29 Sep 2018 01:54:00 +0300 Subject: [PATCH 101/163] Content selector: allow to copy paths to clipboard (feature #2847) --- CHANGELOG.md | 1 + .../contentselector/model/contentmodel.hpp | 4 ++-- .../contentselector/view/contentselector.cpp | 19 +++++++++++++++++++ .../contentselector/view/contentselector.hpp | 1 + files/ui/datafilespage.ui | 2 +- 5 files changed, 24 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 04547e249..7539dcf17 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -135,6 +135,7 @@ Feature #1617: Editor: Enchantment effect record verifier Feature #1645: Casting effects from objects Feature #2606: Editor: Implemented (optional) case sensitive global search + Feature #2847: Content selector: allow to copy the path to a file by using the context menu Feature #3083: Play animation when NPC is casting spell via script Feature #3103: Provide option for disposition to get increased by successful trade Feature #3276: Editor: Search - Show number of (remaining) search results and indicate a search without any results diff --git a/components/contentselector/model/contentmodel.hpp b/components/contentselector/model/contentmodel.hpp index bc785a276..80cd6e4c3 100644 --- a/components/contentselector/model/contentmodel.hpp +++ b/components/contentselector/model/contentmodel.hpp @@ -48,6 +48,8 @@ namespace ContentSelectorModel QModelIndex indexFromItem(const EsmFile *item) const; const EsmFile *item(const QString &name) const; + const EsmFile *item(int row) const; + EsmFile *item(int row); QStringList gameFiles() const; bool isEnabled (QModelIndex index) const; @@ -65,8 +67,6 @@ namespace ContentSelectorModel private: void addFile(EsmFile *file); - const EsmFile *item(int row) const; - EsmFile *item(int row); void sortFiles(); diff --git a/components/contentselector/view/contentselector.cpp b/components/contentselector/view/contentselector.cpp index 595be9f44..e26f9a942 100644 --- a/components/contentselector/view/contentselector.cpp +++ b/components/contentselector/view/contentselector.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -67,6 +68,7 @@ void ContentSelectorView::ContentSelector::buildContextMenu() mContextMenu = new QMenu(ui.addonView); mContextMenu->addAction(tr("&Check Selected"), this, SLOT(slotCheckMultiSelectedItems())); mContextMenu->addAction(tr("&Uncheck Selected"), this, SLOT(slotUncheckMultiSelectedItems())); + mContextMenu->addAction(tr("&Copy Path(s) to Clipboard"), this, SLOT(slotCopySelectedItemsPaths())); } void ContentSelectorView::ContentSelector::setProfileContent(const QStringList &fileList) @@ -245,3 +247,20 @@ void ContentSelectorView::ContentSelector::slotCheckMultiSelectedItems() { setCheckStateForMultiSelectedItems(true); } + +void ContentSelectorView::ContentSelector::slotCopySelectedItemsPaths() +{ + QClipboard *clipboard = QApplication::clipboard(); + QString filepaths; + foreach (const QModelIndex& index, ui.addonView->selectionModel()->selectedIndexes()) + { + int row = mAddonProxyModel->mapToSource(index).row(); + const ContentSelectorModel::EsmFile *file = mContentModel->item(row); + filepaths += file->filePath() + "\n"; + } + + if (!filepaths.isEmpty()) + { + clipboard->setText(filepaths); + } +} diff --git a/components/contentselector/view/contentselector.hpp b/components/contentselector/view/contentselector.hpp index 9f9ad886c..bc47224e4 100644 --- a/components/contentselector/view/contentselector.hpp +++ b/components/contentselector/view/contentselector.hpp @@ -70,6 +70,7 @@ namespace ContentSelectorView void slotShowContextMenu(const QPoint& pos); void slotCheckMultiSelectedItems(); void slotUncheckMultiSelectedItems(); + void slotCopySelectedItemsPaths(); }; } diff --git a/files/ui/datafilespage.ui b/files/ui/datafilespage.ui index 5a6b7265a..5c26e1044 100644 --- a/files/ui/datafilespage.ui +++ b/files/ui/datafilespage.ui @@ -62,7 +62,7 @@ - Select a profiile + Select a content list From 87394f2ebbfdfa932b4a651fc0b39eb9ca2f2875 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 29 Sep 2018 16:21:40 +0400 Subject: [PATCH 102/163] Revert "Take in account transformations of NiTriShape and NiSkinData in skinning (bug #4437)" This reverts commit 09427d3f5ec05aaa00f31da272f725f1cdba6976 since the fix is not entirely correct. --- CHANGELOG.md | 1 - components/nifosg/nifloader.cpp | 6 +----- components/sceneutil/riggeometry.cpp | 9 --------- components/sceneutil/riggeometry.hpp | 2 -- 4 files changed, 1 insertion(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bf1e9e5e0..d83d38fd7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -70,7 +70,6 @@ Bug #4431: "Lock 0" console command is a no-op Bug #4432: Guards behaviour is incorrect if they do not have AI packages Bug #4433: Guard behaviour is incorrect with Alarm = 0 - Bug #4437: Transformations for NiSkinInstance are ignored Bug #4451: Script fails to compile when using "Begin, [ScriptName]" syntax Bug #4452: Default terrain texture bleeds through texture transitions Bug #4453: Quick keys behaviour is invalid for equipment diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 81898e477..3198e995c 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -1082,10 +1082,7 @@ namespace NifOsg // Assign bone weights osg::ref_ptr map (new SceneUtil::RigGeometry::InfluenceMap); - // We should take in account transformation of NiTriShape and NiSkinData const Nif::NiSkinData *data = skin->data.getPtr(); - osg::Matrixf shapeTransforms = triShape->trafo.toMatrix() * data->trafo.toMatrix(); - const Nif::NodeList &bones = skin->bones; for(size_t i = 0;i < bones.length();i++) { @@ -1099,12 +1096,11 @@ namespace NifOsg influence.mWeights.insert(indexWeight); } influence.mInvBindMatrix = data->bones[i].trafo.toMatrix(); - influence.mBoundSphere = osg::BoundingSpheref(shapeTransforms * data->bones[i].boundSphereCenter, data->bones[i].boundSphereRadius); + influence.mBoundSphere = osg::BoundingSpheref(data->bones[i].boundSphereCenter, data->bones[i].boundSphereRadius); map->mMap.insert(std::make_pair(boneName, influence)); } rig->setInfluenceMap(map); - rig->setRigTransforms(*&shapeTransforms); parentNode->addChild(rig); } diff --git a/components/sceneutil/riggeometry.cpp b/components/sceneutil/riggeometry.cpp index 94e4a3b3e..c409bcd5c 100644 --- a/components/sceneutil/riggeometry.cpp +++ b/components/sceneutil/riggeometry.cpp @@ -49,7 +49,6 @@ RigGeometry::RigGeometry(const RigGeometry ©, const osg::CopyOp ©op) : Drawable(copy, copyop) , mSkeleton(NULL) , mInfluenceMap(copy.mInfluenceMap) - , mRigTransforms(copy.mRigTransforms) , mLastFrameNumber(0) , mBoundsFirstFrame(true) { @@ -215,9 +214,6 @@ void RigGeometry::cull(osg::NodeVisitor* nv) if (mGeomToSkelMatrix) resultMat *= (*mGeomToSkelMatrix); - if (!mRigTransforms.isIdentity()) - resultMat *= mRigTransforms; - for (auto &vertex : pair.second) { (*positionDst)[vertex] = resultMat.preMult((*positionSrc)[vertex]); @@ -313,11 +309,6 @@ void RigGeometry::setInfluenceMap(osg::ref_ptr influenceMap) mInfluenceMap = influenceMap; } -void RigGeometry::setRigTransforms(osg::Matrixf& transform) -{ - mRigTransforms = transform; -} - void RigGeometry::accept(osg::NodeVisitor &nv) { if (!nv.validNodeMask(*this)) diff --git a/components/sceneutil/riggeometry.hpp b/components/sceneutil/riggeometry.hpp index 723455234..60b3edc9d 100644 --- a/components/sceneutil/riggeometry.hpp +++ b/components/sceneutil/riggeometry.hpp @@ -40,7 +40,6 @@ namespace SceneUtil }; void setInfluenceMap(osg::ref_ptr influenceMap); - void setRigTransforms(osg::Matrixf& transform); /// Initialize this geometry from the source geometry. /// @note The source geometry will not be modified. @@ -66,7 +65,6 @@ namespace SceneUtil osg::ref_ptr mGeomToSkelMatrix; osg::ref_ptr mInfluenceMap; - osg::Matrixf mRigTransforms; typedef std::pair BoneBindMatrixPair; From 674e33170bcd8a5f725d727d37ad48729a6da8a8 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 30 Sep 2018 21:16:02 +0300 Subject: [PATCH 103/163] Fix quick key system regressions (bug #4662) --- apps/openmw/mwgui/quickkeysmenu.cpp | 35 +++++++++++++---------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp index 8041c50c5..23ad87fd6 100644 --- a/apps/openmw/mwgui/quickkeysmenu.cpp +++ b/apps/openmw/mwgui/quickkeysmenu.cpp @@ -254,6 +254,8 @@ namespace MWGui MyGUI::Gui::getInstance().destroyWidget(mSelected->button->getChildAt(0)); mSelected->type = Type_MagicItem; + mSelected->id = item.getCellRef().getRefId(); + mSelected->name = item.getClass().getName(item); mSelected->button->setFrame("textures\\menu_icon_select_magic_magic.dds", MyGUI::IntCoord(2, 2, 40, 40)); mSelected->button->setIcon(item); @@ -271,20 +273,19 @@ namespace MWGui while (mSelected->button->getChildCount()) // Destroy number label MyGUI::Gui::getInstance().destroyWidget(mSelected->button->getChildAt(0)); + const MWWorld::ESMStore &esmStore = MWBase::Environment::get().getWorld()->getStore(); + const ESM::Spell* spell = esmStore.get().find(spellId); + mSelected->type = Type_Magic; mSelected->id = spellId; + mSelected->name = spell->mName; mSelected->button->setItem(MWWorld::Ptr()); mSelected->button->setUserString("ToolTipType", "Spell"); mSelected->button->setUserString("Spell", spellId); - const MWWorld::ESMStore &esmStore = MWBase::Environment::get().getWorld()->getStore(); - // use the icon of the first effect - const ESM::Spell* spell = esmStore.get().find(spellId); - - const ESM::MagicEffect* effect = - esmStore.get().find(spell->mEffects.mList.front().mEffectID); + const ESM::MagicEffect* effect = esmStore.get().find(spell->mEffects.mList.front().mEffectID); std::string path = effect->mIcon; int slashPos = path.rfind('\\'); @@ -423,9 +424,7 @@ namespace MWGui if (!spells.hasSpell(spellId)) { - const ESM::Spell* spell = - MWBase::Environment::get().getWorld()->getStore().get().find(spellId); - MWBase::Environment::get().getWindowManager()->messageBox("#{sQuickMenu5} " + spell->mName); + MWBase::Environment::get().getWindowManager()->messageBox("#{sQuickMenu5} " + key->name); return; } @@ -547,28 +546,26 @@ namespace MWGui return; mSelected = &mKey[i]; - mSelected->type = (QuickKeysMenu::QuickKeyType) it->mType; - mSelected->id = it->mId; - switch (mSelected->type) + switch (it->mType) { case Type_Magic: - if (MWBase::Environment::get().getWorld()->getStore().get().search(mSelected->id)) - onAssignMagic(mSelected->id); + if (MWBase::Environment::get().getWorld()->getStore().get().search(it->mId)) + onAssignMagic(it->mId); break; case Type_Item: case Type_MagicItem: { // Find the item by id - MWWorld::Ptr item = store.findReplacement(mSelected->id); + MWWorld::Ptr item = store.findReplacement(it->mId); if (item.isEmpty()) - unassign(&mKey[i]); + unassign(mSelected); else { - if (mSelected->type == Type_Item) + if (it->mType == Type_Item) onAssignItem(item); - else if (mSelected->type == Type_MagicItem) + else // if (it->mType == Type_MagicItem) onAssignMagicItem(item); } @@ -576,7 +573,7 @@ namespace MWGui } case Type_Unassigned: case Type_HandToHand: - unassign(&mKey[i]); + unassign(mSelected); break; } From 4dc424036f218dda617b98d4ff4218e072da31a9 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Fri, 28 Sep 2018 13:57:13 +0400 Subject: [PATCH 104/163] Cleanup magic effects, when create a new ActorAnimation --- apps/openmw/mwrender/actoranimation.cpp | 3 ++ apps/openmw/mwrender/animation.cpp | 69 ++++++++++++++++++++++--- apps/openmw/mwrender/animation.hpp | 1 + 3 files changed, 67 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwrender/actoranimation.cpp b/apps/openmw/mwrender/actoranimation.cpp index d92b95a71..e6aff8d19 100644 --- a/apps/openmw/mwrender/actoranimation.cpp +++ b/apps/openmw/mwrender/actoranimation.cpp @@ -40,6 +40,9 @@ ActorAnimation::ActorAnimation(const MWWorld::Ptr& ptr, osg::ref_ptr addHiddenItemLight(*iter, light); } } + + // Make sure we cleaned object from effects, just in cast if we re-use node + removeEffects(); } ActorAnimation::~ActorAnimation() diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 7410dbf03..e7c3f2200 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -213,14 +213,12 @@ namespace RemoveFinishedCallbackVisitor() : RemoveVisitor() , mHasMagicEffects(false) - , mEffectId(-1) { } RemoveFinishedCallbackVisitor(int effectId) : RemoveVisitor() , mHasMagicEffects(false) - , mEffectId(effectId) { } @@ -240,9 +238,63 @@ namespace MWRender::UpdateVfxCallback* vfxCallback = dynamic_cast(callback); if (vfxCallback) { - bool finished = vfxCallback->mFinished; - bool toRemove = mEffectId >= 0 && vfxCallback->mParams.mEffectId == mEffectId; - if (finished || toRemove) + if (vfxCallback->mFinished) + mToRemove.push_back(std::make_pair(group.asNode(), group.getParent(0))); + else + mHasMagicEffects = true; + } + } + } + + virtual void apply(osg::MatrixTransform &node) + { + traverse(node); + } + + virtual void apply(osg::Geometry&) + { + } + + private: + int mEffectId; + }; + + class RemoveCallbackVisitor : public RemoveVisitor + { + public: + bool mHasMagicEffects; + + RemoveCallbackVisitor() + : RemoveVisitor() + , mHasMagicEffects(false) + , mEffectId(-1) + { + } + + RemoveCallbackVisitor(int effectId) + : RemoveVisitor() + , mHasMagicEffects(false) + , mEffectId(effectId) + { + } + + virtual void apply(osg::Node &node) + { + traverse(node); + } + + virtual void apply(osg::Group &group) + { + traverse(group); + + osg::Callback* callback = group.getUpdateCallback(); + if (callback) + { + MWRender::UpdateVfxCallback* vfxCallback = dynamic_cast(callback); + if (vfxCallback) + { + bool toRemove = mEffectId < 0 || vfxCallback->mParams.mEffectId == mEffectId; + if (toRemove) mToRemove.push_back(std::make_pair(group.asNode(), group.getParent(0))); else mHasMagicEffects = true; @@ -1649,12 +1701,17 @@ namespace MWRender void Animation::removeEffect(int effectId) { - RemoveFinishedCallbackVisitor visitor(effectId); + RemoveCallbackVisitor visitor(effectId); mInsert->accept(visitor); visitor.remove(); mHasMagicEffects = visitor.mHasMagicEffects; } + void Animation::removeEffects() + { + removeEffect(-1); + } + void Animation::getLoopingEffects(std::vector &out) const { if (!mHasMagicEffects) diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index e10d2c995..5828bd52f 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -370,6 +370,7 @@ public: */ void addEffect (const std::string& model, int effectId, bool loop = false, const std::string& bonename = "", const std::string& texture = "", float scale = 1.0f); void removeEffect (int effectId); + void removeEffects (); void getLoopingEffects (std::vector& out) const; // Add a spell casting glow to an object. From measuring video taken from the original engine, From 632045e145c4162a5cc095a77db3ee498791b14b Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sun, 30 Sep 2018 10:35:01 +0400 Subject: [PATCH 105/163] Improve the 'part has no parents' warning --- apps/openmw/mwrender/animation.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index e7c3f2200..85d4aae9d 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -1975,12 +1975,12 @@ namespace MWRender PartHolder::~PartHolder() { if (mNode.get() && !mNode->getNumParents()) - Log(Debug::Verbose) << "Part has no parents" ; + Log(Debug::Verbose) << "Part \"" << mNode->getName() << "\" has no parents" ; if (mNode.get() && mNode->getNumParents()) { if (mNode->getNumParents() > 1) - Log(Debug::Verbose) << "Part has multiple (" << mNode->getNumParents() << ") parents"; + Log(Debug::Verbose) << "Part \"" << mNode->getName() << "\" has multiple (" << mNode->getNumParents() << ") parents"; mNode->getParent(0)->removeChild(mNode); } } From 3896a2eba6da63a227094b1cc38fd3a3e22c4c26 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sun, 30 Sep 2018 11:12:28 +0400 Subject: [PATCH 106/163] Do not use a PartHolder for spell effect node --- apps/openmw/mwrender/animation.cpp | 5 ++--- apps/openmw/mwrender/animation.hpp | 1 - 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 85d4aae9d..840e59290 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -624,8 +624,8 @@ namespace MWRender } else { - // Remove effect immediately - mParams.mObjects.reset(); + // Hide effect immediately + node->setNodeMask(0); mFinished = true; } } @@ -1686,7 +1686,6 @@ namespace MWRender params.mLoop = loop; params.mEffectId = effectId; params.mBoneName = bonename; - params.mObjects = PartHolderPtr(new PartHolder(node)); params.mAnimTime = std::shared_ptr(new EffectAnimationTime); trans->addUpdateCallback(new UpdateVfxCallback(params)); diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 5828bd52f..2d8ac152f 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -74,7 +74,6 @@ typedef std::shared_ptr PartHolderPtr; struct EffectParams { std::string mModelName; // Just here so we don't add the same effect twice - PartHolderPtr mObjects; std::shared_ptr mAnimTime; float mMaxControllerLength; int mEffectId; From 07ccc5abdb3f18d946a57d34ddd9161ef297bee2 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Mon, 1 Oct 2018 21:57:13 +0400 Subject: [PATCH 107/163] Remove non-looping effects after rest --- apps/openmw/mwmechanics/actors.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 92f2ba34d..4f051f40a 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -1683,7 +1683,10 @@ namespace MWMechanics MWRender::Animation* animation = MWBase::Environment::get().getWorld()->getAnimation(iter->first); if (animation) - animation->updateEffects(); + { + animation->removeEffects(); + MWBase::Environment::get().getWorld()->applyLoopingParticles(iter->first); + } } From 34e45efac3968850e1db2e3c88fcb0bffe78fcd3 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Fri, 5 Oct 2018 16:29:57 +0300 Subject: [PATCH 108/163] Fix first person swimming animations --- apps/openmw/mwmechanics/character.cpp | 35 ++++++++++++++++++--------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 90f90e9e2..c4975b308 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -432,26 +432,37 @@ void CharacterController::refreshMovementAnims(const WeaponInfo* weap, Character if(movestate != sMovementListEnd) { movementAnimName = movestate->groupname; - if(weap != sWeaponTypeListEnd && movementAnimName.find("swim") == std::string::npos) + if(weap != sWeaponTypeListEnd) { - if (mWeaponType == WeapType_Spell && (movement == CharState_TurnLeft || movement == CharState_TurnRight)) // Spellcasting stance turning is a special case - movementAnimName = weap->shortgroup + movementAnimName; - else - movementAnimName += weap->shortgroup; + std::string::size_type swimpos = movementAnimName.find("swim"); + if (swimpos == std::string::npos) + { + if (mWeaponType == WeapType_Spell && (movement == CharState_TurnLeft || movement == CharState_TurnRight)) // Spellcasting stance turning is a special case + movementAnimName = weap->shortgroup + movementAnimName; + else + movementAnimName += weap->shortgroup; + } if(!mAnimation->hasAnimation(movementAnimName)) { movemask = MWRender::Animation::BlendMask_LowerBody; movementAnimName = movestate->groupname; - // Since we apply movement only for lower body, do not reset idle animations. - // For upper body there will be idle animation. - if (idle == CharState_None) - idle = CharState_Idle; + if (swimpos == std::string::npos) + { + // Since we apply movement only for lower body, do not reset idle animations. + // For upper body there will be idle animation. + if (idle == CharState_None) + idle = CharState_Idle; - // For crossbow animations use 1h ones as fallback - if (mWeaponType == WeapType_Crossbow) - movementAnimName += "1h"; + // For crossbow animations use 1h ones as fallback + if (mWeaponType == WeapType_Crossbow) + movementAnimName += "1h"; + } + else if (idle == CharState_None) + { + idle = CharState_IdleSwim; + } } } } From 03788edd6399dc86ff5eee50f990c49ef1333260 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Fri, 5 Oct 2018 18:48:17 +0300 Subject: [PATCH 109/163] Fix redundant drag call in instance dragging (bug #4593) --- CHANGELOG.md | 1 + apps/opencs/view/render/worldspacewidget.cpp | 5 ----- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bf1e9e5e0..2214a4f20 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -115,6 +115,7 @@ Bug #4575: Weird result of attack animation blending with movement animations Bug #4576: Reset of idle animations when attack can not be started Bug #4591: Attack strength should be 0 if player did not hold the attack button + Bug #4593: Editor: Instance dragging is broken Bug #4597: <> operator causes a compile error Bug #4604: Picking up gold from the ground only makes 1 grabbed Bug #4607: Scaling for animated collision shapes is applied twice diff --git a/apps/opencs/view/render/worldspacewidget.cpp b/apps/opencs/view/render/worldspacewidget.cpp index 5b825fab1..7e405266e 100644 --- a/apps/opencs/view/render/worldspacewidget.cpp +++ b/apps/opencs/view/render/worldspacewidget.cpp @@ -648,11 +648,6 @@ void CSVRender::WorldspaceWidget::mouseMoveEvent (QMouseEvent *event) mDragX = event->posF().x(); mDragY = height() - event->posF().y(); #endif - - if (mDragMode == InteractionType_PrimaryEdit) - { - editMode.drag (event->pos(), mDragX, mDragY, mDragFactor); // note: terraintexturemode only uses pos - } } } else From 768c532b8f41bf9e98d5136edc11550bc58616ad Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 7 Oct 2018 19:41:59 +0300 Subject: [PATCH 110/163] Editor: display light source color as a color (bug #4668) --- CHANGELOG.md | 1 + apps/opencs/model/world/refidcollection.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e8c835a58..226f09374 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -133,6 +133,7 @@ Bug #4649: Levelup fully restores health Bug #4653: Length of non-ASCII strings is handled incorrectly in ESM reader Bug #4654: Editor: UpdateVisitor does not initialize skeletons for animated objects + Bug #4668: Editor: Light source color is displayed as an integer Feature #912: Editor: Add missing icons to UniversalId tables Feature #1221: Editor: Creature/NPC rendering Feature #1617: Editor: Enchantment effect record verifier diff --git a/apps/opencs/model/world/refidcollection.cpp b/apps/opencs/model/world/refidcollection.cpp index 6716c7240..272722f64 100644 --- a/apps/opencs/model/world/refidcollection.cpp +++ b/apps/opencs/model/world/refidcollection.cpp @@ -437,7 +437,7 @@ CSMWorld::RefIdCollection::RefIdCollection() mColumns.push_back (RefIdColumn (Columns::ColumnId_Radius, ColumnBase::Display_Integer)); lightColumns.mRadius = &mColumns.back(); - mColumns.push_back (RefIdColumn (Columns::ColumnId_Colour, ColumnBase::Display_Integer)); + mColumns.push_back (RefIdColumn (Columns::ColumnId_Colour, ColumnBase::Display_Colour)); lightColumns.mColor = &mColumns.back(); mColumns.push_back (RefIdColumn (Columns::ColumnId_Sound, ColumnBase::Display_Sound)); From e627f49df438b1cc7792f6d7fe080d991e5e0a57 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 7 Oct 2018 20:22:52 +0300 Subject: [PATCH 111/163] Fix AppVeyor build --- CI/before_script.msvc.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CI/before_script.msvc.sh b/CI/before_script.msvc.sh index 2c4fe6312..8ee4b7fd9 100644 --- a/CI/before_script.msvc.sh +++ b/CI/before_script.msvc.sh @@ -346,7 +346,7 @@ if [ -z $SKIP_DOWNLOAD ]; then # OpenAL download "OpenAL-Soft 1.17.2" \ - "http://kcat.strangesoft.net/openal-binaries/openal-soft-1.17.2-bin.zip" \ + "http://openal-soft.org/openal-binaries/openal-soft-1.17.2-bin.zip" \ "OpenAL-Soft-1.17.2.zip" # OSG From cae2e84ab45b70e5690666de9649fe3e08b5f888 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Mon, 8 Oct 2018 15:50:54 +0300 Subject: [PATCH 112/163] Trace the player down after enabling collision with TCL (bug #4669) --- CHANGELOG.md | 2 ++ apps/openmw/mwworld/worldimp.cpp | 10 ++++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 226f09374..448009926 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ Bug #3049: Drain and Fortify effects are not properly applied on health, magicka and fatigue Bug #3059: Unable to hit with marksman weapons when too close to an enemy Bug #3072: Fatal error on AddItem that has a script containing Equip + Bug #3219: NPC and creature initial position tracing down limit is too small Bug #3249: Fixed revert function not updating views properly Bug #3288: TrueType fonts are handled incorrectly Bug #3374: Touch spells not hitting kwama foragers @@ -134,6 +135,7 @@ Bug #4653: Length of non-ASCII strings is handled incorrectly in ESM reader Bug #4654: Editor: UpdateVisitor does not initialize skeletons for animated objects Bug #4668: Editor: Light source color is displayed as an integer + Bug #4669: ToggleCollision should trace the player down after collision being enabled Feature #912: Editor: Add missing icons to UniversalId tables Feature #1221: Editor: Creature/NPC rendering Feature #1617: Editor: Enchantment effect record verifier diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 3cb6b3a59..78eddf28f 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1343,7 +1343,7 @@ namespace MWWorld if (force || !isFlying(ptr)) { - osg::Vec3f traced = mPhysics->traceDown(ptr, pos, 500); + osg::Vec3f traced = mPhysics->traceDown(ptr, pos, Constants::CellSizeInUnits); if (traced.z() < pos.z()) pos.z() = traced.z(); } @@ -1581,7 +1581,13 @@ namespace MWWorld bool World::toggleCollisionMode() { - return mPhysics->toggleCollisionMode(); + if (mPhysics->toggleCollisionMode()) + { + adjustPosition(getPlayerPtr(), true); + return true; + } + + return false; } bool World::toggleRenderMode (MWRender::RenderMode mode) From 09aecb955c5b526936f9102d792e0b189f9048c3 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Mon, 8 Oct 2018 17:06:30 +0300 Subject: [PATCH 113/163] getSkill usage cleanup (bug #4671) --- CHANGELOG.md | 1 + apps/openmw/mwclass/ingredient.cpp | 5 +---- apps/openmw/mwclass/npc.cpp | 8 ++++---- apps/openmw/mwgui/recharge.cpp | 4 +--- apps/openmw/mwgui/trainingwindow.cpp | 8 +++----- apps/openmw/mwmechanics/alchemy.cpp | 7 ++----- apps/openmw/mwmechanics/enchanting.cpp | 9 ++++----- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 4 ++-- apps/openmw/mwmechanics/repair.cpp | 4 +--- apps/openmw/mwmechanics/security.cpp | 4 +--- apps/openmw/mwmechanics/spellcasting.cpp | 3 +-- apps/openmw/mwmechanics/spellpriority.cpp | 4 ++-- apps/openmw/mwworld/inventorystore.cpp | 5 ++--- 13 files changed, 25 insertions(+), 41 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 226f09374..8d8f539a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -134,6 +134,7 @@ Bug #4653: Length of non-ASCII strings is handled incorrectly in ESM reader Bug #4654: Editor: UpdateVisitor does not initialize skeletons for animated objects Bug #4668: Editor: Light source color is displayed as an integer + Bug #4671: knownEffect functions should use modified Alchemy skill Feature #912: Editor: Add missing icons to UniversalId tables Feature #1221: Editor: Creature/NPC rendering Feature #1617: Editor: Enchantment effect record verifier diff --git a/apps/openmw/mwclass/ingredient.cpp b/apps/openmw/mwclass/ingredient.cpp index c133b6a26..70339564c 100644 --- a/apps/openmw/mwclass/ingredient.cpp +++ b/apps/openmw/mwclass/ingredient.cpp @@ -14,8 +14,6 @@ #include "../mwworld/actioneat.hpp" #include "../mwworld/nullaction.hpp" -#include "../mwmechanics/npcstats.hpp" - #include "../mwgui/tooltips.hpp" #include "../mwrender/objects.hpp" @@ -134,8 +132,7 @@ namespace MWClass } MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); - MWMechanics::NpcStats& npcStats = player.getClass().getNpcStats (player); - int alchemySkill = npcStats.getSkill (ESM::Skill::Alchemy).getBase(); + int alchemySkill = player.getClass().getSkill(player, ESM::Skill::Alchemy); static const float fWortChanceValue = MWBase::Environment::get().getWorld()->getStore().get().find("fWortChanceValue")->mValue.getFloat(); diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 4979db2fd..c0f6eb64f 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -949,7 +949,7 @@ namespace MWClass if(sneaking) walkSpeed *= gmst.fSneakSpeedMultiplier->mValue.getFloat(); - float runSpeed = walkSpeed*(0.01f * npcdata->mNpcStats.getSkill(ESM::Skill::Athletics).getModified() * + float runSpeed = walkSpeed*(0.01f * getSkill(ptr, ESM::Skill::Athletics) * gmst.fAthleticsRunBonus->mValue.getFloat() + gmst.fBaseRunMultiplier->mValue.getFloat()); float moveSpeed; @@ -971,7 +971,7 @@ namespace MWClass if(running) swimSpeed = runSpeed; swimSpeed *= 1.0f + 0.01f * mageffects.get(ESM::MagicEffect::SwiftSwim).getMagnitude(); - swimSpeed *= gmst.fSwimRunBase->mValue.getFloat() + 0.01f*npcdata->mNpcStats.getSkill(ESM::Skill::Athletics).getModified()* + swimSpeed *= gmst.fSwimRunBase->mValue.getFloat() + 0.01f*getSkill(ptr, ESM::Skill::Athletics)* gmst.fSwimRunAthleticsMult->mValue.getFloat(); moveSpeed = swimSpeed; } @@ -1004,7 +1004,7 @@ namespace MWClass gmst.fJumpEncumbranceMultiplier->mValue.getFloat() * (1.0f - Npc::getNormalizedEncumbrance(ptr)); - float a = static_cast(npcdata->mNpcStats.getSkill(ESM::Skill::Acrobatics).getModified()); + float a = static_cast(getSkill(ptr, ESM::Skill::Acrobatics)); float b = 0.0f; if(a > 50.0f) { @@ -1129,7 +1129,7 @@ namespace MWClass float fUnarmoredBase1 = store.find("fUnarmoredBase1")->mValue.getFloat(); float fUnarmoredBase2 = store.find("fUnarmoredBase2")->mValue.getFloat(); - int unarmoredSkill = stats.getSkill(ESM::Skill::Unarmored).getModified(); + int unarmoredSkill = getSkill(ptr, ESM::Skill::Unarmored); float ratings[MWWorld::InventoryStore::Slots]; for(int i = 0;i < MWWorld::InventoryStore::Slots;i++) diff --git a/apps/openmw/mwgui/recharge.cpp b/apps/openmw/mwgui/recharge.cpp index a6ed16bd4..672db3e32 100644 --- a/apps/openmw/mwgui/recharge.cpp +++ b/apps/openmw/mwgui/recharge.cpp @@ -18,7 +18,6 @@ #include "../mwworld/esmstore.hpp" #include "../mwmechanics/creaturestats.hpp" -#include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/actorutil.hpp" #include "itemwidget.hpp" @@ -139,7 +138,6 @@ void Recharge::onItemClicked(MyGUI::Widget *sender, const MWWorld::Ptr& item) MWWorld::Ptr player = MWMechanics::getPlayer(); MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player); - MWMechanics::NpcStats& npcStats = player.getClass().getNpcStats(player); float luckTerm = 0.1f * stats.getAttribute(ESM::Attribute::Luck).getModified(); if (luckTerm < 1|| luckTerm > 10) @@ -152,7 +150,7 @@ void Recharge::onItemClicked(MyGUI::Widget *sender, const MWWorld::Ptr& item) if (intelligenceTerm < 1) intelligenceTerm = 1; - float x = (npcStats.getSkill(ESM::Skill::Enchant).getModified() + intelligenceTerm + luckTerm) * stats.getFatigueTerm(); + float x = (player.getClass().getSkill(player, ESM::Skill::Enchant) + intelligenceTerm + luckTerm) * stats.getFatigueTerm(); int roll = Misc::Rng::roll0to99(); if (roll < x) { diff --git a/apps/openmw/mwgui/trainingwindow.cpp b/apps/openmw/mwgui/trainingwindow.cpp index b309da27d..60e6584c7 100644 --- a/apps/openmw/mwgui/trainingwindow.cpp +++ b/apps/openmw/mwgui/trainingwindow.cpp @@ -73,14 +73,12 @@ namespace MWGui mPlayerGold->setCaptionWithReplacing("#{sGold}: " + MyGUI::utility::toString(playerGold)); - MWMechanics::NpcStats& npcStats = actor.getClass().getNpcStats (actor); - // NPC can train you in his best 3 skills std::vector< std::pair > skills; for (int i=0; i player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId)) return; - MWMechanics::NpcStats& npcStats = mPtr.getClass().getNpcStats (mPtr); - if (npcStats.getSkill (skillId).getModified () <= pcStats.getSkill (skillId).getBase ()) + if (mPtr.getClass().getSkill(mPtr, skillId) <= pcStats.getSkill (skillId).getBase ()) { MWBase::Environment::get().getWindowManager()->messageBox ("#{sServiceTrainingWords}"); return; @@ -168,6 +165,7 @@ namespace MWGui player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, price, player); // add gold to NPC trading gold pool + MWMechanics::NpcStats& npcStats = mPtr.getClass().getNpcStats(mPtr); npcStats.setGoldPool(npcStats.getGoldPool() + price); // advance time diff --git a/apps/openmw/mwmechanics/alchemy.cpp b/apps/openmw/mwmechanics/alchemy.cpp index 98860198b..c199bfb3f 100644 --- a/apps/openmw/mwmechanics/alchemy.cpp +++ b/apps/openmw/mwmechanics/alchemy.cpp @@ -26,7 +26,6 @@ #include "magiceffects.hpp" #include "creaturestats.hpp" -#include "npcstats.hpp" MWMechanics::Alchemy::Alchemy() : mValue(0) @@ -318,10 +317,9 @@ void MWMechanics::Alchemy::increaseSkill() float MWMechanics::Alchemy::getAlchemyFactor() const { const CreatureStats& creatureStats = mAlchemist.getClass().getCreatureStats (mAlchemist); - const NpcStats& npcStats = mAlchemist.getClass().getNpcStats (mAlchemist); return - (npcStats.getSkill (ESM::Skill::Alchemy).getModified() + + (mAlchemist.getClass().getSkill(mAlchemist, ESM::Skill::Alchemy) + 0.1f * creatureStats.getAttribute (ESM::Attribute::Intelligence).getModified() + 0.1f * creatureStats.getAttribute (ESM::Attribute::Luck).getModified()); } @@ -472,8 +470,7 @@ MWMechanics::Alchemy::TEffectsIterator MWMechanics::Alchemy::endEffects() const bool MWMechanics::Alchemy::knownEffect(unsigned int potionEffectIndex, const MWWorld::Ptr &npc) { - MWMechanics::NpcStats& npcStats = npc.getClass().getNpcStats(npc); - int alchemySkill = npcStats.getSkill (ESM::Skill::Alchemy).getBase(); + int alchemySkill = npc.getClass().getSkill (npc, ESM::Skill::Alchemy); static const float fWortChanceValue = MWBase::Environment::get().getWorld()->getStore().get().find("fWortChanceValue")->mValue.getFloat(); return (potionEffectIndex <= 1 && alchemySkill >= fWortChanceValue) diff --git a/apps/openmw/mwmechanics/enchanting.cpp b/apps/openmw/mwmechanics/enchanting.cpp index c0bffc4cb..e4bb0cf3f 100644 --- a/apps/openmw/mwmechanics/enchanting.cpp +++ b/apps/openmw/mwmechanics/enchanting.cpp @@ -10,7 +10,6 @@ #include "../mwbase/mechanicsmanager.hpp" #include "creaturestats.hpp" -#include "npcstats.hpp" #include "spellcasting.hpp" #include "actorutil.hpp" @@ -280,11 +279,11 @@ namespace MWMechanics float Enchanting::getEnchantChance() const { - const NpcStats& npcStats = mEnchanter.getClass().getNpcStats (mEnchanter); + const CreatureStats& stats = mEnchanter.getClass().getCreatureStats(mEnchanter); - float chance1 = (npcStats.getSkill (ESM::Skill::Enchant).getModified() + - (0.25f * npcStats.getAttribute (ESM::Attribute::Intelligence).getModified()) - + (0.125f * npcStats.getAttribute (ESM::Attribute::Luck).getModified())); + float chance1 = (mEnchanter.getClass().getSkill(mEnchanter, ESM::Skill::Enchant) + + (0.25f * stats.getAttribute (ESM::Attribute::Intelligence).getModified()) + + (0.125f * stats.getAttribute (ESM::Attribute::Luck).getModified())); const MWWorld::Store& gmst = MWBase::Environment::get().getWorld()->getStore().get(); diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 33357b79a..1ea40023b 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -639,10 +639,10 @@ namespace MWMechanics // I suppose the temporary disposition change (second param to getDerivedDisposition()) _has_ to be considered here, // otherwise one would get different prices when exiting and re-entering the dialogue window... int clampedDisposition = getDerivedDisposition(ptr); - float a = static_cast(std::min(playerStats.getSkill(ESM::Skill::Mercantile).getModified(), 100)); + float a = static_cast(std::min(playerPtr.getClass().getSkill(playerPtr, ESM::Skill::Mercantile), 100)); float b = std::min(0.1f * playerStats.getAttribute(ESM::Attribute::Luck).getModified(), 10.f); float c = std::min(0.2f * playerStats.getAttribute(ESM::Attribute::Personality).getModified(), 10.f); - float d = static_cast(std::min(sellerStats.getSkill(ESM::Skill::Mercantile).getModified(), 100)); + float d = static_cast(std::min(ptr.getClass().getSkill(ptr, ESM::Skill::Mercantile), 100)); float e = std::min(0.1f * sellerStats.getAttribute(ESM::Attribute::Luck).getModified(), 10.f); float f = std::min(0.2f * sellerStats.getAttribute(ESM::Attribute::Personality).getModified(), 10.f); float pcTerm = (clampedDisposition - 50 + a + b + c) * playerStats.getFatigueTerm(); diff --git a/apps/openmw/mwmechanics/repair.cpp b/apps/openmw/mwmechanics/repair.cpp index 3ebef36bf..8c5e69896 100644 --- a/apps/openmw/mwmechanics/repair.cpp +++ b/apps/openmw/mwmechanics/repair.cpp @@ -14,7 +14,6 @@ #include "../mwworld/esmstore.hpp" #include "creaturestats.hpp" -#include "npcstats.hpp" #include "actorutil.hpp" namespace MWMechanics @@ -34,12 +33,11 @@ void Repair::repair(const MWWorld::Ptr &itemToRepair) mTool.getCellRef().setCharge(uses-1); MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player); - MWMechanics::NpcStats& npcStats = player.getClass().getNpcStats(player); float fatigueTerm = stats.getFatigueTerm(); int pcStrength = stats.getAttribute(ESM::Attribute::Strength).getModified(); int pcLuck = stats.getAttribute(ESM::Attribute::Luck).getModified(); - int armorerSkill = npcStats.getSkill(ESM::Skill::Armorer).getModified(); + int armorerSkill = player.getClass().getSkill(player, ESM::Skill::Armorer); float fRepairAmountMult = MWBase::Environment::get().getWorld()->getStore().get() .find("fRepairAmountMult")->mValue.getFloat(); diff --git a/apps/openmw/mwmechanics/security.cpp b/apps/openmw/mwmechanics/security.cpp index c8a4dd770..18d4ec2e5 100644 --- a/apps/openmw/mwmechanics/security.cpp +++ b/apps/openmw/mwmechanics/security.cpp @@ -11,7 +11,6 @@ #include "../mwbase/windowmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" -#include "npcstats.hpp" #include "creaturestats.hpp" namespace MWMechanics @@ -21,10 +20,9 @@ namespace MWMechanics : mActor(actor) { CreatureStats& creatureStats = actor.getClass().getCreatureStats(actor); - NpcStats& npcStats = actor.getClass().getNpcStats(actor); mAgility = static_cast(creatureStats.getAttribute(ESM::Attribute::Agility).getModified()); mLuck = static_cast(creatureStats.getAttribute(ESM::Attribute::Luck).getModified()); - mSecuritySkill = static_cast(npcStats.getSkill(ESM::Skill::Security).getModified()); + mSecuritySkill = static_cast(actor.getClass().getSkill(actor, ESM::Skill::Security)); mFatigueTerm = creatureStats.getFatigueTerm(); } diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index f1ee2520b..f6eb497e7 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -964,10 +964,9 @@ namespace MWMechanics MWBase::Environment::get().getWorld()->getStore().get().find ( effect.mEffectID); - const MWMechanics::NpcStats& npcStats = mCaster.getClass().getNpcStats(mCaster); const MWMechanics::CreatureStats& creatureStats = mCaster.getClass().getCreatureStats(mCaster); - float x = (npcStats.getSkill (ESM::Skill::Alchemy).getModified() + + float x = (mCaster.getClass().getSkill(mCaster, ESM::Skill::Alchemy) + 0.2f * creatureStats.getAttribute (ESM::Attribute::Intelligence).getModified() + 0.1f * creatureStats.getAttribute (ESM::Attribute::Luck).getModified()) * creatureStats.getFatigueTerm(); diff --git a/apps/openmw/mwmechanics/spellpriority.cpp b/apps/openmw/mwmechanics/spellpriority.cpp index 807b56608..3b242266c 100644 --- a/apps/openmw/mwmechanics/spellpriority.cpp +++ b/apps/openmw/mwmechanics/spellpriority.cpp @@ -14,7 +14,7 @@ #include "../mwworld/actionequip.hpp" #include "../mwworld/cellstore.hpp" -#include "npcstats.hpp" +#include "creaturestats.hpp" #include "spellcasting.hpp" #include "combat.hpp" @@ -538,7 +538,7 @@ namespace MWMechanics case ESM::MagicEffect::DrainSkill: if (enemy.isEmpty() || !enemy.getClass().isNpc()) return 0.f; - if (enemy.getClass().getNpcStats(enemy).getSkill(effect.mSkill).getModified() <= 0) + if (enemy.getClass().getSkill(enemy, effect.mSkill) <= 0) return 0.f; break; diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 7f3018c55..59ec6ab3e 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -250,11 +250,10 @@ void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& actor) { const MWBase::World *world = MWBase::Environment::get().getWorld(); const MWWorld::Store &store = world->getStore().get(); - MWMechanics::NpcStats& stats = actor.getClass().getNpcStats(actor); static float fUnarmoredBase1 = store.find("fUnarmoredBase1")->mValue.getFloat(); static float fUnarmoredBase2 = store.find("fUnarmoredBase2")->mValue.getFloat(); - int unarmoredSkill = stats.getSkill(ESM::Skill::Unarmored).getModified(); + int unarmoredSkill = actor.getClass().getSkill(actor, ESM::Skill::Unarmored); float unarmoredRating = (fUnarmoredBase1 * unarmoredSkill) * (fUnarmoredBase2 * unarmoredSkill); @@ -384,7 +383,7 @@ void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& actor) for (int j = 0; j < static_cast(weaponSkillsLength); ++j) { - int skillValue = stats.getSkill(static_cast(weaponSkills[j])).getModified(); + int skillValue = actor.getClass().getSkill(actor, static_cast(weaponSkills[j])); if (skillValue > max && !weaponSkillVisited[j]) { From e06f0b797a596c2773018ecf3ddf0a7981767ede Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 9 Oct 2018 10:21:12 +0400 Subject: [PATCH 114/163] Replace all NULLs to nullptr --- apps/opencs/editor.cpp | 4 +- apps/opencs/model/doc/operationholder.cpp | 2 +- apps/opencs/model/world/columnimp.cpp | 2 +- apps/opencs/model/world/commands.cpp | 2 +- .../model/world/idcompletionmanager.cpp | 2 +- apps/opencs/model/world/idtableproxymodel.cpp | 10 +-- .../model/world/infotableproxymodel.cpp | 6 +- apps/opencs/model/world/refidadapterimp.cpp | 34 ++++----- apps/opencs/model/world/resourcesmanager.cpp | 2 +- apps/opencs/view/doc/filedialog.cpp | 4 +- apps/opencs/view/doc/subview.hpp | 2 +- apps/opencs/view/doc/view.cpp | 10 +-- apps/opencs/view/doc/view.hpp | 2 +- apps/opencs/view/render/cameracontroller.cpp | 4 +- apps/opencs/view/render/object.cpp | 2 +- .../view/render/pagedworldspacewidget.cpp | 2 +- apps/opencs/view/render/scenewidget.cpp | 8 +- apps/opencs/view/render/terrainstorage.cpp | 2 +- apps/opencs/view/widget/colorpickerpopup.cpp | 2 +- apps/opencs/view/widget/completerpopup.cpp | 2 +- apps/opencs/view/world/cellcreator.cpp | 2 +- apps/opencs/view/world/dialoguesubview.cpp | 28 +++---- apps/opencs/view/world/dragdroputils.cpp | 2 +- apps/opencs/view/world/dragrecordtable.cpp | 2 +- apps/opencs/view/world/dragrecordtable.hpp | 2 +- .../world/extendedcommandconfigurator.cpp | 2 +- .../view/world/idcompletiondelegate.cpp | 2 +- apps/opencs/view/world/nestedtable.cpp | 6 +- apps/opencs/view/world/nestedtable.hpp | 2 +- apps/opencs/view/world/scenesubview.cpp | 10 +-- apps/opencs/view/world/util.cpp | 4 +- apps/openmw/android_main.c | 4 +- apps/openmw/engine.cpp | 12 +-- apps/openmw/mwclass/container.cpp | 2 +- apps/openmw/mwclass/creature.cpp | 4 +- apps/openmw/mwclass/light.cpp | 2 +- apps/openmw/mwclass/npc.cpp | 4 +- apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 2 +- apps/openmw/mwdialogue/filter.cpp | 2 +- apps/openmw/mwdialogue/journalentry.cpp | 2 +- apps/openmw/mwgui/alchemywindow.cpp | 2 +- apps/openmw/mwgui/backgroundimage.cpp | 2 +- apps/openmw/mwgui/backgroundimage.hpp | 2 +- apps/openmw/mwgui/bookpage.cpp | 68 ++++++++--------- apps/openmw/mwgui/class.cpp | 12 +-- apps/openmw/mwgui/companionwindow.cpp | 12 +-- apps/openmw/mwgui/container.cpp | 16 ++-- apps/openmw/mwgui/countdialog.cpp | 4 +- apps/openmw/mwgui/cursor.cpp | 4 +- apps/openmw/mwgui/draganddrop.cpp | 8 +- apps/openmw/mwgui/enchantingdialog.cpp | 2 +- apps/openmw/mwgui/exposedwindow.cpp | 2 +- apps/openmw/mwgui/formatting.cpp | 2 +- apps/openmw/mwgui/hud.cpp | 30 ++++---- apps/openmw/mwgui/inventorywindow.cpp | 14 ++-- apps/openmw/mwgui/itemchargeview.cpp | 4 +- apps/openmw/mwgui/itemmodel.cpp | 6 +- apps/openmw/mwgui/itemselection.cpp | 4 +- apps/openmw/mwgui/itemview.cpp | 6 +- apps/openmw/mwgui/itemwidget.cpp | 8 +- apps/openmw/mwgui/journalviewmodel.cpp | 2 +- apps/openmw/mwgui/loadingscreen.cpp | 4 +- apps/openmw/mwgui/mainmenu.cpp | 14 ++-- apps/openmw/mwgui/mapwindow.cpp | 18 ++--- apps/openmw/mwgui/messagebox.cpp | 22 +++--- apps/openmw/mwgui/race.cpp | 12 +-- apps/openmw/mwgui/recharge.cpp | 4 +- apps/openmw/mwgui/repair.cpp | 4 +- apps/openmw/mwgui/review.cpp | 2 +- apps/openmw/mwgui/savegamedialog.cpp | 24 +++--- apps/openmw/mwgui/spellcreationdialog.cpp | 12 +-- apps/openmw/mwgui/spellmodel.cpp | 2 +- apps/openmw/mwgui/spellview.cpp | 12 +-- apps/openmw/mwgui/spellwindow.cpp | 4 +- apps/openmw/mwgui/statswindow.cpp | 4 +- apps/openmw/mwgui/tradewindow.cpp | 12 +-- apps/openmw/mwgui/videowidget.cpp | 2 +- apps/openmw/mwgui/waitdialog.cpp | 2 +- apps/openmw/mwgui/widgets.cpp | 22 +++--- apps/openmw/mwgui/windowbase.cpp | 4 +- apps/openmw/mwgui/windowbase.hpp | 2 +- apps/openmw/mwgui/windowmanagerimp.cpp | 74 +++++++++---------- apps/openmw/mwgui/windowpinnablebase.cpp | 2 +- apps/openmw/mwinput/inputmanagerimp.cpp | 8 +- apps/openmw/mwmechanics/actors.cpp | 4 +- apps/openmw/mwmechanics/aicombat.cpp | 2 +- apps/openmw/mwmechanics/aicombat.hpp | 2 +- apps/openmw/mwmechanics/aicombataction.cpp | 2 +- apps/openmw/mwmechanics/aicombataction.hpp | 2 +- apps/openmw/mwmechanics/aipackage.cpp | 2 +- apps/openmw/mwmechanics/aipackage.hpp | 2 +- apps/openmw/mwmechanics/aistate.hpp | 8 +- apps/openmw/mwmechanics/aiwander.hpp | 2 +- apps/openmw/mwmechanics/autocalcspell.cpp | 2 +- apps/openmw/mwmechanics/character.cpp | 14 ++-- apps/openmw/mwmechanics/character.hpp | 4 +- .../mwmechanics/mechanicsmanagerimp.cpp | 2 +- apps/openmw/mwmechanics/objects.cpp | 2 +- apps/openmw/mwmechanics/obstacle.hpp | 2 +- apps/openmw/mwmechanics/pathfinding.cpp | 4 +- apps/openmw/mwmechanics/pathgrid.cpp | 4 +- apps/openmw/mwmechanics/spellcasting.cpp | 6 +- apps/openmw/mwmechanics/spellcasting.hpp | 8 +- apps/openmw/mwphysics/physicssystem.cpp | 16 ++-- apps/openmw/mwphysics/trace.cpp | 2 +- apps/openmw/mwrender/animation.cpp | 32 ++++---- apps/openmw/mwrender/animation.hpp | 4 +- apps/openmw/mwrender/bulletdebugdraw.cpp | 6 +- apps/openmw/mwrender/camera.cpp | 2 +- apps/openmw/mwrender/characterpreview.cpp | 6 +- apps/openmw/mwrender/creatureanimation.cpp | 4 +- apps/openmw/mwrender/globalmap.cpp | 2 +- apps/openmw/mwrender/landmanager.cpp | 4 +- apps/openmw/mwrender/landmanager.hpp | 2 +- apps/openmw/mwrender/npcanimation.cpp | 16 ++-- apps/openmw/mwrender/npcanimation.hpp | 8 +- apps/openmw/mwrender/objects.cpp | 14 ++-- apps/openmw/mwrender/pathgrid.cpp | 10 +-- apps/openmw/mwrender/renderingmanager.cpp | 10 +-- apps/openmw/mwrender/ripplesimulation.cpp | 2 +- apps/openmw/mwrender/sky.cpp | 30 ++++---- apps/openmw/mwrender/water.cpp | 10 +-- apps/openmw/mwrender/weaponanimation.cpp | 4 +- apps/openmw/mwscript/statsextensions.cpp | 2 +- .../mwscript/transformationextensions.cpp | 2 +- apps/openmw/mwsound/ffmpeg_decoder.cpp | 54 +++++++------- apps/openmw/mwsound/movieaudiofactory.cpp | 2 +- apps/openmw/mwsound/openal_output.cpp | 6 +- apps/openmw/mwsound/soundmanagerimp.cpp | 2 +- apps/openmw/mwstate/charactermanager.cpp | 4 +- apps/openmw/mwstate/quicksavemanager.cpp | 4 +- apps/openmw/mwstate/quicksavemanager.hpp | 2 +- apps/openmw/mwstate/statemanagerimp.cpp | 2 +- apps/openmw/mwworld/cellpreloader.cpp | 4 +- apps/openmw/mwworld/cells.cpp | 2 +- apps/openmw/mwworld/cellstore.cpp | 6 +- apps/openmw/mwworld/cellstore.hpp | 4 +- apps/openmw/mwworld/cellvisitors.hpp | 2 +- apps/openmw/mwworld/containerstore.cpp | 4 +- apps/openmw/mwworld/inventorystore.cpp | 2 +- apps/openmw/mwworld/livecellref.hpp | 4 +- apps/openmw/mwworld/player.cpp | 4 +- apps/openmw/mwworld/player.hpp | 2 +- apps/openmw/mwworld/scene.cpp | 6 +- apps/openmw/mwworld/store.cpp | 14 ++-- apps/openmw/mwworld/store.hpp | 2 +- apps/openmw/mwworld/worldimp.cpp | 10 +-- apps/openmw_test_suite/mwworld/test_store.cpp | 4 +- apps/wizard/existinginstallationpage.cpp | 2 +- .../contentselector/view/contentselector.cpp | 2 +- components/crashcatcher/crashcatcher.cpp | 24 +++--- components/debug/debugging.cpp | 2 +- components/esm/esmreader.cpp | 4 +- components/esm/esmwriter.cpp | 4 +- components/esm/loadland.cpp | 6 +- components/esm/loadland.hpp | 4 +- components/esmterrain/storage.cpp | 2 +- components/files/androidpath.cpp | 6 +- components/files/linuxpath.cpp | 6 +- components/files/lowlevelfile.cpp | 26 +++---- components/files/macospath.cpp | 6 +- components/files/windowspath.cpp | 6 +- components/myguiplatform/additivelayer.cpp | 2 +- components/myguiplatform/myguidatamanager.cpp | 2 +- .../myguiplatform/myguirendermanager.cpp | 14 ++-- .../myguiplatform/myguirendermanager.hpp | 2 +- components/myguiplatform/myguitexture.cpp | 2 +- components/nif/niffile.cpp | 6 +- components/nif/node.hpp | 4 +- components/nif/recordptr.hpp | 10 +-- components/nifbullet/bulletnifloader.cpp | 14 ++-- components/nifosg/nifloader.cpp | 28 +++---- components/nifosg/particle.cpp | 4 +- components/resource/bulletshape.cpp | 4 +- components/resource/bulletshapemanager.cpp | 2 +- components/resource/multiobjectcache.hpp | 2 +- components/sceneutil/clone.cpp | 2 +- components/sceneutil/controller.cpp | 2 +- components/sceneutil/lightmanager.cpp | 16 ++-- components/sceneutil/lightmanager.hpp | 2 +- components/sceneutil/lightutil.cpp | 2 +- components/sceneutil/optimizer.cpp | 8 +- components/sceneutil/riggeometry.cpp | 8 +- components/sceneutil/skeleton.cpp | 8 +- components/sceneutil/statesetupdater.cpp | 4 +- components/sceneutil/visitor.cpp | 2 +- components/sceneutil/visitor.hpp | 4 +- components/sceneutil/workqueue.cpp | 2 +- components/sceneutil/workqueue.hpp | 2 +- components/sdlutil/sdlcursormanager.cpp | 2 +- components/sdlutil/sdlgraphicswindow.cpp | 8 +- components/sdlutil/sdlinputwrapper.cpp | 10 +-- components/shader/shadermanager.cpp | 6 +- components/shader/shadermanager.hpp | 2 +- components/shader/shadervisitor.cpp | 22 +++--- components/terrain/chunkmanager.cpp | 2 +- components/terrain/compositemaprenderer.cpp | 2 +- components/terrain/quadtreenode.cpp | 4 +- components/terrain/quadtreeworld.cpp | 10 +-- components/terrain/terraindrawable.hpp | 2 +- components/terrain/terraingrid.cpp | 6 +- components/terrain/viewdata.cpp | 10 +-- components/terrain/world.hpp | 2 +- components/translation/translation.cpp | 2 +- components/widgets/windowcaption.cpp | 6 +- 205 files changed, 717 insertions(+), 721 deletions(-) diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index f29c6aca8..450b434e6 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -22,7 +22,7 @@ CS::Editor::Editor (int argc, char **argv) : mSettingsState (mCfgMgr), mDocumentManager (mCfgMgr), mViewManager (mDocumentManager), mPid(""), mLock(), mMerge (mDocumentManager), - mIpcServerName ("org.openmw.OpenCS"), mServer(NULL), mClientSocket(NULL) + mIpcServerName ("org.openmw.OpenCS"), mServer(nullptr), mClientSocket(nullptr) { std::pair > config = readConfig(); @@ -339,7 +339,7 @@ bool CS::Editor::makeIPCServer() } mServer->close(); - mServer = NULL; + mServer = nullptr; return false; } diff --git a/apps/opencs/model/doc/operationholder.cpp b/apps/opencs/model/doc/operationholder.cpp index ccbed6c8b..0fd2bef95 100644 --- a/apps/opencs/model/doc/operationholder.cpp +++ b/apps/opencs/model/doc/operationholder.cpp @@ -3,7 +3,7 @@ #include "operation.hpp" CSMDoc::OperationHolder::OperationHolder (Operation *operation) - : mOperation(NULL) + : mOperation(nullptr) , mRunning (false) { if (operation) diff --git a/apps/opencs/model/world/columnimp.cpp b/apps/opencs/model/world/columnimp.cpp index 49e4bebe6..b202a97d9 100644 --- a/apps/opencs/model/world/columnimp.cpp +++ b/apps/opencs/model/world/columnimp.cpp @@ -318,7 +318,7 @@ namespace CSMWorld QVariant BodyPartRaceColumn::get(const Record &record) const { - if (mMeshType != NULL && mMeshType->get(record) == ESM::BodyPart::MT_Skin) + if (mMeshType != nullptr && mMeshType->get(record) == ESM::BodyPart::MT_Skin) { return QString::fromUtf8(record.get().mRace.c_str()); } diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index 4133b2050..4c7096479 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -244,7 +244,7 @@ void CSMWorld::CreateCommand::applyModifications() if (!mNestedValues.empty()) { CSMWorld::IdTree *tree = dynamic_cast(&mModel); - if (tree == NULL) + if (tree == nullptr) { throw std::logic_error("CSMWorld::CreateCommand: Attempt to add nested values to the non-nested model"); } diff --git a/apps/opencs/model/world/idcompletionmanager.cpp b/apps/opencs/model/world/idcompletionmanager.cpp index 9fa6e3add..649a96038 100644 --- a/apps/opencs/model/world/idcompletionmanager.cpp +++ b/apps/opencs/model/world/idcompletionmanager.cpp @@ -92,7 +92,7 @@ void CSMWorld::IdCompletionManager::generateCompleters(CSMWorld::Data &data) { QAbstractItemModel *model = data.getTableModel(current->second); CSMWorld::IdTableBase *table = dynamic_cast(model); - if (table != NULL) + if (table != nullptr) { int idColumn = table->searchColumnIndex(CSMWorld::Columns::ColumnId_Id); if (idColumn != -1) diff --git a/apps/opencs/model/world/idtableproxymodel.cpp b/apps/opencs/model/world/idtableproxymodel.cpp index 6d50e9edb..534e5dd63 100644 --- a/apps/opencs/model/world/idtableproxymodel.cpp +++ b/apps/opencs/model/world/idtableproxymodel.cpp @@ -18,7 +18,7 @@ namespace void CSMWorld::IdTableProxyModel::updateColumnMap() { - Q_ASSERT(mSourceModel != NULL); + Q_ASSERT(mSourceModel != nullptr); mColumnMap.clear(); if (mFilter) @@ -33,7 +33,7 @@ void CSMWorld::IdTableProxyModel::updateColumnMap() bool CSMWorld::IdTableProxyModel::filterAcceptsRow (int sourceRow, const QModelIndex& sourceParent) const { - Q_ASSERT(mSourceModel != NULL); + Q_ASSERT(mSourceModel != nullptr); // It is not possible to use filterAcceptsColumn() and check for // sourceModel()->headerData (sourceColumn, Qt::Horizontal, CSMWorld::ColumnBase::Role_Flags) @@ -51,14 +51,14 @@ bool CSMWorld::IdTableProxyModel::filterAcceptsRow (int sourceRow, const QModelI CSMWorld::IdTableProxyModel::IdTableProxyModel (QObject *parent) : QSortFilterProxyModel (parent), - mSourceModel(NULL) + mSourceModel(nullptr) { setSortCaseSensitivity (Qt::CaseInsensitive); } QModelIndex CSMWorld::IdTableProxyModel::getModelIndex (const std::string& id, int column) const { - Q_ASSERT(mSourceModel != NULL); + Q_ASSERT(mSourceModel != nullptr); return mapFromSource(mSourceModel->getModelIndex (id, column)); } @@ -113,7 +113,7 @@ bool CSMWorld::IdTableProxyModel::lessThan(const QModelIndex &left, const QModel QString CSMWorld::IdTableProxyModel::getRecordId(int sourceRow) const { - Q_ASSERT(mSourceModel != NULL); + Q_ASSERT(mSourceModel != nullptr); int idColumn = mSourceModel->findColumnIndex(Columns::ColumnId_Id); return mSourceModel->data(mSourceModel->index(sourceRow, idColumn)).toString(); diff --git a/apps/opencs/model/world/infotableproxymodel.cpp b/apps/opencs/model/world/infotableproxymodel.cpp index 4f0b2e752..7c6a9c323 100644 --- a/apps/opencs/model/world/infotableproxymodel.cpp +++ b/apps/opencs/model/world/infotableproxymodel.cpp @@ -28,7 +28,7 @@ void CSMWorld::InfoTableProxyModel::setSourceModel(QAbstractItemModel *sourceMod { IdTableProxyModel::setSourceModel(sourceModel); - if (mSourceModel != NULL) + if (mSourceModel != nullptr) { mInfoColumnIndex = mSourceModel->findColumnIndex(mInfoColumnId); mFirstRowCache.clear(); @@ -37,7 +37,7 @@ void CSMWorld::InfoTableProxyModel::setSourceModel(QAbstractItemModel *sourceMod bool CSMWorld::InfoTableProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const { - Q_ASSERT(mSourceModel != NULL); + Q_ASSERT(mSourceModel != nullptr); QModelIndex first = mSourceModel->index(getFirstInfoRow(left.row()), left.column()); QModelIndex second = mSourceModel->index(getFirstInfoRow(right.row()), right.column()); @@ -52,7 +52,7 @@ bool CSMWorld::InfoTableProxyModel::lessThan(const QModelIndex &left, const QMod int CSMWorld::InfoTableProxyModel::getFirstInfoRow(int currentRow) const { - Q_ASSERT(mSourceModel != NULL); + Q_ASSERT(mSourceModel != nullptr); int row = currentRow; int column = mInfoColumnIndex; diff --git a/apps/opencs/model/world/refidadapterimp.cpp b/apps/opencs/model/world/refidadapterimp.cpp index 5ac9ecb18..1948a95cb 100644 --- a/apps/opencs/model/world/refidadapterimp.cpp +++ b/apps/opencs/model/world/refidadapterimp.cpp @@ -453,13 +453,13 @@ void CSMWorld::ContainerRefIdAdapter::setData (const RefIdColumn *column, RefIdD CSMWorld::CreatureColumns::CreatureColumns (const ActorColumns& actorColumns) : ActorColumns (actorColumns), - mType(NULL), - mScale(NULL), - mOriginal(NULL), - mAttributes(NULL), - mAttacks(NULL), - mMisc(NULL), - mBloodType(NULL) + mType(nullptr), + mScale(nullptr), + mOriginal(nullptr), + mAttributes(nullptr), + mAttacks(nullptr), + mMisc(nullptr), + mBloodType(nullptr) {} CSMWorld::CreatureRefIdAdapter::CreatureRefIdAdapter (const CreatureColumns& columns) @@ -748,16 +748,16 @@ void CSMWorld::MiscRefIdAdapter::setData (const RefIdColumn *column, RefIdData& CSMWorld::NpcColumns::NpcColumns (const ActorColumns& actorColumns) : ActorColumns (actorColumns), - mRace(NULL), - mClass(NULL), - mFaction(NULL), - mHair(NULL), - mHead(NULL), - mAttributes(NULL), - mSkills(NULL), - mMisc(NULL), - mBloodType(NULL), - mGender(NULL) + mRace(nullptr), + mClass(nullptr), + mFaction(nullptr), + mHair(nullptr), + mHead(nullptr), + mAttributes(nullptr), + mSkills(nullptr), + mMisc(nullptr), + mBloodType(nullptr), + mGender(nullptr) {} CSMWorld::NpcRefIdAdapter::NpcRefIdAdapter (const NpcColumns& columns) diff --git a/apps/opencs/model/world/resourcesmanager.cpp b/apps/opencs/model/world/resourcesmanager.cpp index 62dfe53a9..1af9c5e9b 100644 --- a/apps/opencs/model/world/resourcesmanager.cpp +++ b/apps/opencs/model/world/resourcesmanager.cpp @@ -3,7 +3,7 @@ #include CSMWorld::ResourcesManager::ResourcesManager() - : mVFS(NULL) + : mVFS(nullptr) { } diff --git a/apps/opencs/view/doc/filedialog.cpp b/apps/opencs/view/doc/filedialog.cpp index 3c5f20202..7693276c6 100644 --- a/apps/opencs/view/doc/filedialog.cpp +++ b/apps/opencs/view/doc/filedialog.cpp @@ -189,7 +189,7 @@ void CSVDoc::FileDialog::slotRejected() if(mFileWidget) { delete mFileWidget; - mFileWidget = NULL; + mFileWidget = nullptr; } close(); } @@ -200,7 +200,7 @@ void CSVDoc::FileDialog::slotNewFile() if(mFileWidget) { delete mFileWidget; - mFileWidget = NULL; + mFileWidget = nullptr; } disconnect (ui.projectButtonBox, SIGNAL (accepted()), this, SLOT (slotNewFile())); close(); diff --git a/apps/opencs/view/doc/subview.hpp b/apps/opencs/view/doc/subview.hpp index 8402bf79a..44b81743f 100644 --- a/apps/opencs/view/doc/subview.hpp +++ b/apps/opencs/view/doc/subview.hpp @@ -64,7 +64,7 @@ namespace CSVDoc void updateTitle(); - void updateSubViewIndices (SubView *view = NULL); + void updateSubViewIndices (SubView *view = nullptr); void universalIdChanged (const CSMWorld::UniversalId& universalId); diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index ca90b11ff..ed4dcff76 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -400,7 +400,7 @@ void CSVDoc::View::updateSubViewIndices(SubView *view) else { delete subView->titleBarWidget(); - subView->setTitleBarWidget (NULL); + subView->setTitleBarWidget (nullptr); } } } @@ -429,7 +429,7 @@ void CSVDoc::View::updateActions() CSVDoc::View::View (ViewManager& viewManager, CSMDoc::Document *document, int totalViews) : mViewManager (viewManager), mDocument (document), mViewIndex (totalViews-1), - mViewTotal (totalViews), mScroll(NULL), mScrollbarOnly(false) + mViewTotal (totalViews), mScroll(nullptr), mScrollbarOnly(false) { CSMPrefs::Category& windows = CSMPrefs::State::get()["Windows"]; @@ -563,7 +563,7 @@ void CSVDoc::View::addSubView (const CSMWorld::UniversalId& id, const std::strin return; } - SubView *view = NULL; + SubView *view = nullptr; if(isReferenceable) { view = mSubViewFactory.makeSubView (CSMWorld::UniversalId(CSMWorld::UniversalId::Type_Referenceable, id.getId()), *mDocument); @@ -631,7 +631,7 @@ void CSVDoc::View::moveScrollBarToEnd(int min, int max) void CSVDoc::View::settingChanged (const CSMPrefs::Setting *setting) { if (*setting=="Windows/hide-subview") - updateSubViewIndices (NULL); + updateSubViewIndices (nullptr); else if (*setting=="Windows/mainwindow-scrollbar") { if (setting->toString()!="Grow Only") @@ -659,7 +659,7 @@ void CSVDoc::View::settingChanged (const CSMPrefs::Setting *setting) mScroll->takeWidget(); setCentralWidget (&mSubViewWindow); mScroll->deleteLater(); - mScroll = NULL; + mScroll = nullptr; } } } diff --git a/apps/opencs/view/doc/view.hpp b/apps/opencs/view/doc/view.hpp index 76c81b964..7a9a48b0f 100644 --- a/apps/opencs/view/doc/view.hpp +++ b/apps/opencs/view/doc/view.hpp @@ -149,7 +149,7 @@ namespace CSVDoc void updateTitle(); // called when subviews are added or removed - void updateSubViewIndices (SubView *view = NULL); + void updateSubViewIndices (SubView *view = nullptr); private slots: diff --git a/apps/opencs/view/render/cameracontroller.cpp b/apps/opencs/view/render/cameracontroller.cpp index 37f439fd3..524a79821 100644 --- a/apps/opencs/view/render/cameracontroller.cpp +++ b/apps/opencs/view/render/cameracontroller.cpp @@ -38,7 +38,7 @@ namespace CSVRender , mCameraSensitivity(1/650.f) , mSecondaryMoveMult(50) , mWheelMoveMult(8) - , mCamera(NULL) + , mCamera(nullptr) { } @@ -81,7 +81,7 @@ namespace CSVRender bool wasActive = mActive; mCamera = camera; - mActive = (mCamera != NULL); + mActive = (mCamera != nullptr); if (mActive != wasActive) { diff --git a/apps/opencs/view/render/object.cpp b/apps/opencs/view/render/object.cpp index 36d80fa4b..026625795 100644 --- a/apps/opencs/view/render/object.cpp +++ b/apps/opencs/view/render/object.cpp @@ -86,7 +86,7 @@ void CSVRender::Object::update() const int ModelIndex = referenceables.findColumnIndex (CSMWorld::Columns::ColumnId_Model); int index = referenceables.searchId (mReferenceableId); - const ESM::Light* light = NULL; + const ESM::Light* light = nullptr; mBaseNode->removeChildren(0, mBaseNode->getNumChildren()); diff --git a/apps/opencs/view/render/pagedworldspacewidget.cpp b/apps/opencs/view/render/pagedworldspacewidget.cpp index ccea70761..ff69656a2 100644 --- a/apps/opencs/view/render/pagedworldspacewidget.cpp +++ b/apps/opencs/view/render/pagedworldspacewidget.cpp @@ -527,7 +527,7 @@ void CSVRender::PagedWorldspaceWidget::addCellToSceneFromCamera (int offsetX, in CSVRender::PagedWorldspaceWidget::PagedWorldspaceWidget (QWidget* parent, CSMDoc::Document& document) : WorldspaceWidget (document, parent), mDocument (document), mWorldspace ("std::default"), - mControlElements(NULL), mDisplayCellCoord(true) + mControlElements(nullptr), mDisplayCellCoord(true) { QAbstractItemModel *cells = document.getData().getTableModel (CSMWorld::UniversalId::Type_Cells); diff --git a/apps/opencs/view/render/scenewidget.cpp b/apps/opencs/view/render/scenewidget.cpp index 0d1780d57..6cc64f653 100644 --- a/apps/opencs/view/render/scenewidget.cpp +++ b/apps/opencs/view/render/scenewidget.cpp @@ -185,7 +185,7 @@ SceneWidget::SceneWidget(std::shared_ptr resourceSyste bool retrieveInput) : RenderWidget(parent, f) , mResourceSystem(resourceSystem) - , mLighting(NULL) + , mLighting(nullptr) , mHasDefaultAmbient(false) , mPrevMouseX(0) , mPrevMouseY(0) @@ -425,21 +425,21 @@ void SceneWidget::selectNavigationMode (const std::string& mode) { if (mode=="1st") { - mCurrentCamControl->setCamera(NULL); + mCurrentCamControl->setCamera(nullptr); mCurrentCamControl = mFreeCamControl; mFreeCamControl->setCamera(getCamera()); mFreeCamControl->fixUpAxis(CameraController::WorldUp); } else if (mode=="free") { - mCurrentCamControl->setCamera(NULL); + mCurrentCamControl->setCamera(nullptr); mCurrentCamControl = mFreeCamControl; mFreeCamControl->setCamera(getCamera()); mFreeCamControl->unfixUpAxis(); } else if (mode=="orbit") { - mCurrentCamControl->setCamera(NULL); + mCurrentCamControl->setCamera(nullptr); mCurrentCamControl = mOrbitCamControl; mOrbitCamControl->setCamera(getCamera()); mOrbitCamControl->reset(); diff --git a/apps/opencs/view/render/terrainstorage.cpp b/apps/opencs/view/render/terrainstorage.cpp index 51c9dd009..e0edae774 100644 --- a/apps/opencs/view/render/terrainstorage.cpp +++ b/apps/opencs/view/render/terrainstorage.cpp @@ -18,7 +18,7 @@ namespace CSVRender // has to wrap the vertices of the last row and column to the next cell, which may be a nonexisting cell int index = mData.getLand().searchId(CSMWorld::Land::createUniqueRecordId(cellX, cellY)); if (index == -1) - return NULL; + return nullptr; const ESM::Land& land = mData.getLand().getRecord(index).get(); return new ESMTerrain::LandObject(&land, ESM::Land::DATA_VHGT | ESM::Land::DATA_VNML | ESM::Land::DATA_VCLR | ESM::Land::DATA_VTEX); diff --git a/apps/opencs/view/widget/colorpickerpopup.cpp b/apps/opencs/view/widget/colorpickerpopup.cpp index 47aab8981..a38728ef3 100644 --- a/apps/opencs/view/widget/colorpickerpopup.cpp +++ b/apps/opencs/view/widget/colorpickerpopup.cpp @@ -45,7 +45,7 @@ void CSVWidget::ColorPickerPopup::showPicker(const QPoint &position, const QColo void CSVWidget::ColorPickerPopup::mousePressEvent(QMouseEvent *event) { QPushButton *button = qobject_cast(parentWidget()); - if (button != NULL) + if (button != nullptr) { QStyleOptionButton option; option.init(button); diff --git a/apps/opencs/view/widget/completerpopup.cpp b/apps/opencs/view/widget/completerpopup.cpp index 5777325c8..be509bcb9 100644 --- a/apps/opencs/view/widget/completerpopup.cpp +++ b/apps/opencs/view/widget/completerpopup.cpp @@ -11,7 +11,7 @@ CSVWidget::CompleterPopup::CompleterPopup(QWidget *parent) int CSVWidget::CompleterPopup::sizeHintForRow(int row) const { - if (model() == NULL) + if (model() == nullptr) { return -1; } diff --git a/apps/opencs/view/world/cellcreator.cpp b/apps/opencs/view/world/cellcreator.cpp index 2a710a940..a42e7ead4 100644 --- a/apps/opencs/view/world/cellcreator.cpp +++ b/apps/opencs/view/world/cellcreator.cpp @@ -25,7 +25,7 @@ std::string CSVWorld::CellCreator::getId() const void CSVWorld::CellCreator::configureCreateCommand(CSMWorld::CreateCommand& command) const { CSMWorld::IdTree *model = dynamic_cast(getData().getTableModel(getCollectionId())); - Q_ASSERT(model != NULL); + Q_ASSERT(model != nullptr); int parentIndex = model->findColumnIndex(CSMWorld::Columns::ColumnId_Cell); int index = model->findNestedColumnIndex(parentIndex, CSMWorld::Columns::ColumnId_Interior); command.addNestedValue(parentIndex, index, mType->currentIndex() == 0); diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index 7b198056c..b32e2c7a1 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -158,7 +158,7 @@ mNotEditableDelegate(table, parent) CSVWorld::CommandDelegate* CSVWorld::DialogueDelegateDispatcher::makeDelegate(CSMWorld::ColumnBase::Display display) { - CommandDelegate *delegate = NULL; + CommandDelegate *delegate = nullptr; std::map::const_iterator delegateIt(mDelegates.find(display)); if (delegateIt == mDelegates.end()) { @@ -251,11 +251,11 @@ QWidget* CSVWorld::DialogueDelegateDispatcher::makeEditor(CSMWorld::ColumnBase:: variant = index.data(Qt::DisplayRole); if (!variant.isValid()) { - return NULL; + return nullptr; } } - QWidget* editor = NULL; + QWidget* editor = nullptr; if (! (mTable->flags (index) & Qt::ItemIsEditable)) { return mNotEditableDelegate.createEditor(qobject_cast(mParent), @@ -325,7 +325,7 @@ CSVWorld::IdContextMenu::IdContextMenu(QWidget *widget, CSMWorld::ColumnBase::Di mWidget(widget), mIdType(CSMWorld::TableMimeData::convertEnums(display)) { - Q_ASSERT(mWidget != NULL); + Q_ASSERT(mWidget != nullptr); Q_ASSERT(CSMWorld::ColumnBase::isId(display)); Q_ASSERT(mIdType != CSMWorld::UniversalId::Type_None); @@ -339,7 +339,7 @@ CSVWorld::IdContextMenu::IdContextMenu(QWidget *widget, CSMWorld::ColumnBase::Di connect(mEditIdAction, SIGNAL(triggered()), this, SLOT(editIdRequest())); QLineEdit *lineEdit = qobject_cast(mWidget); - if (lineEdit != NULL) + if (lineEdit != nullptr) { mContextMenu = lineEdit->createStandardContextMenu(); } @@ -360,11 +360,11 @@ QString CSVWorld::IdContextMenu::getWidgetValue() const QLabel *label = qobject_cast(mWidget); QString value = ""; - if (lineEdit != NULL) + if (lineEdit != nullptr) { value = lineEdit->text(); } - else if (label != NULL) + else if (label != nullptr) { value = label->text(); } @@ -436,7 +436,7 @@ void CSVWorld::EditWidget::createEditorContextMenu(QWidget *editor, CSMWorld::ColumnBase::Display display, int currentRow) const { - Q_ASSERT(editor != NULL); + Q_ASSERT(editor != nullptr); if (CSMWorld::ColumnBase::isId(display) && CSMWorld::TableMimeData::convertEnums(display) != CSMWorld::UniversalId::Type_None) @@ -470,11 +470,11 @@ CSVWorld::EditWidget::EditWidget(QWidget *parent, int row, CSMWorld::IdTable* table, CSMWorld::CommandDispatcher& commandDispatcher, CSMDoc::Document& document, bool createAndDelete) : QScrollArea(parent), -mWidgetMapper(NULL), -mNestedTableMapper(NULL), -mDispatcher(NULL), -mNestedTableDispatcher(NULL), -mMainWidget(NULL), +mWidgetMapper(nullptr), +mNestedTableMapper(nullptr), +mDispatcher(nullptr), +mNestedTableDispatcher(nullptr), +mMainWidget(nullptr), mTable(table), mCommandDispatcher (commandDispatcher), mDocument (document) @@ -733,7 +733,7 @@ bool CSVWorld::SimpleDialogueSubView::isLocked() const CSVWorld::SimpleDialogueSubView::SimpleDialogueSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document) : SubView (id), mEditWidget(0), - mMainLayout(NULL), + mMainLayout(nullptr), mTable(dynamic_cast(document.getData().getTableModel(id))), mLocked(false), mDocument(document), diff --git a/apps/opencs/view/world/dragdroputils.cpp b/apps/opencs/view/world/dragdroputils.cpp index 7f3974e53..789d4f33d 100644 --- a/apps/opencs/view/world/dragdroputils.cpp +++ b/apps/opencs/view/world/dragdroputils.cpp @@ -12,7 +12,7 @@ const CSMWorld::TableMimeData *CSVWorld::DragDropUtils::getTableMimeData(const Q bool CSVWorld::DragDropUtils::canAcceptData(const QDropEvent &event, CSMWorld::ColumnBase::Display type) { const CSMWorld::TableMimeData *data = getTableMimeData(event); - return data != NULL && data->holdsType(type); + return data != nullptr && data->holdsType(type); } CSMWorld::UniversalId CSVWorld::DragDropUtils::getAcceptedData(const QDropEvent &event, diff --git a/apps/opencs/view/world/dragrecordtable.cpp b/apps/opencs/view/world/dragrecordtable.cpp index 6d980e171..d795bd5de 100644 --- a/apps/opencs/view/world/dragrecordtable.cpp +++ b/apps/opencs/view/world/dragrecordtable.cpp @@ -83,7 +83,7 @@ void CSVWorld::DragRecordTable::dropEvent(QDropEvent *event) CSMWorld::ColumnBase::Display CSVWorld::DragRecordTable::getIndexDisplayType(const QModelIndex &index) const { - Q_ASSERT(model() != NULL); + Q_ASSERT(model() != nullptr); if (index.isValid()) { diff --git a/apps/opencs/view/world/dragrecordtable.hpp b/apps/opencs/view/world/dragrecordtable.hpp index 560864ba5..9e29b6145 100644 --- a/apps/opencs/view/world/dragrecordtable.hpp +++ b/apps/opencs/view/world/dragrecordtable.hpp @@ -28,7 +28,7 @@ namespace CSVWorld bool mEditLock; public: - DragRecordTable(CSMDoc::Document& document, QWidget* parent = NULL); + DragRecordTable(CSMDoc::Document& document, QWidget* parent = nullptr); virtual std::vector getDraggedRecords() const = 0; diff --git a/apps/opencs/view/world/extendedcommandconfigurator.cpp b/apps/opencs/view/world/extendedcommandconfigurator.cpp index 2cf6222a6..894742024 100644 --- a/apps/opencs/view/world/extendedcommandconfigurator.cpp +++ b/apps/opencs/view/world/extendedcommandconfigurator.cpp @@ -95,7 +95,7 @@ void CSVWorld::ExtendedCommandConfigurator::setupGroupLayout() int divider = 1; do { - while (layout->itemAt(0) != NULL) + while (layout->itemAt(0) != nullptr) { layout->removeItem(layout->itemAt(0)); } diff --git a/apps/opencs/view/world/idcompletiondelegate.cpp b/apps/opencs/view/world/idcompletiondelegate.cpp index 7f0f4ae46..4ff850b9f 100644 --- a/apps/opencs/view/world/idcompletiondelegate.cpp +++ b/apps/opencs/view/world/idcompletiondelegate.cpp @@ -25,7 +25,7 @@ QWidget *CSVWorld::IdCompletionDelegate::createEditor(QWidget *parent, { if (!index.data(Qt::EditRole).isValid() && !index.data(Qt::DisplayRole).isValid()) { - return NULL; + return nullptr; } // The completer for InfoCondVar needs to return a completer based on the first column diff --git a/apps/opencs/view/world/nestedtable.cpp b/apps/opencs/view/world/nestedtable.cpp index 02bd93920..1b72211e8 100644 --- a/apps/opencs/view/world/nestedtable.cpp +++ b/apps/opencs/view/world/nestedtable.cpp @@ -23,9 +23,9 @@ CSVWorld::NestedTable::NestedTable(CSMDoc::Document& document, bool editable, bool fixedRows) : DragRecordTable(document, parent), - mAddNewRowAction(NULL), - mRemoveRowAction(NULL), - mEditIdAction(NULL), + mAddNewRowAction(nullptr), + mRemoveRowAction(nullptr), + mEditIdAction(nullptr), mModel(model) { mDispatcher = new CSMWorld::CommandDispatcher (document, id, this); diff --git a/apps/opencs/view/world/nestedtable.hpp b/apps/opencs/view/world/nestedtable.hpp index 765060ea5..b39c7e560 100644 --- a/apps/opencs/view/world/nestedtable.hpp +++ b/apps/opencs/view/world/nestedtable.hpp @@ -38,7 +38,7 @@ namespace CSVWorld NestedTable(CSMDoc::Document& document, CSMWorld::UniversalId id, CSMWorld::NestedTableProxyModel* model, - QWidget* parent = NULL, + QWidget* parent = nullptr, bool editable = true, bool fixedRows = false); diff --git a/apps/opencs/view/world/scenesubview.cpp b/apps/opencs/view/world/scenesubview.cpp index b14d708da..b03cf8fdb 100644 --- a/apps/opencs/view/world/scenesubview.cpp +++ b/apps/opencs/view/world/scenesubview.cpp @@ -27,7 +27,7 @@ #include "creator.hpp" CSVWorld::SceneSubView::SceneSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document) -: SubView (id), mScene(NULL), mLayout(new QHBoxLayout), mDocument(document), mToolbar(NULL) +: SubView (id), mScene(nullptr), mLayout(new QHBoxLayout), mDocument(document), mToolbar(nullptr) { QVBoxLayout *layout = new QVBoxLayout; @@ -35,7 +35,7 @@ CSVWorld::SceneSubView::SceneSubView (const CSMWorld::UniversalId& id, CSMDoc::D mLayout->setContentsMargins (QMargins (0, 0, 0, 0)); - CSVRender::WorldspaceWidget* worldspaceWidget = NULL; + CSVRender::WorldspaceWidget* worldspaceWidget = nullptr; widgetType whatWidget; if (id.getId()==ESM::CellId::sDefaultWorldspace) @@ -189,9 +189,9 @@ void CSVWorld::SceneSubView::cellSelectionChanged (const CSMWorld::CellSelection void CSVWorld::SceneSubView::handleDrop (const std::vector< CSMWorld::UniversalId >& universalIdData) { - CSVRender::PagedWorldspaceWidget* pagedNewWidget = NULL; - CSVRender::UnpagedWorldspaceWidget* unPagedNewWidget = NULL; - CSVWidget::SceneToolbar* toolbar = NULL; + CSVRender::PagedWorldspaceWidget* pagedNewWidget = nullptr; + CSVRender::UnpagedWorldspaceWidget* unPagedNewWidget = nullptr; + CSVWidget::SceneToolbar* toolbar = nullptr; CSVRender::WorldspaceWidget::DropType type = CSVRender::WorldspaceWidget::getDropType (universalIdData); diff --git a/apps/opencs/view/world/util.cpp b/apps/opencs/view/world/util.cpp index b194e460d..8cdb2d2db 100644 --- a/apps/opencs/view/world/util.cpp +++ b/apps/opencs/view/world/util.cpp @@ -129,7 +129,7 @@ void CSVWorld::CommandDelegate::setModelDataImp (QWidget *editor, QAbstractItemM // Color columns use a custom editor, so we need to fetch selected color from it. CSVWidget::ColorEditor *colorEditor = qobject_cast(editor); - if (colorEditor != NULL) + if (colorEditor != nullptr) { variant = colorEditor->colorInt(); } @@ -322,7 +322,7 @@ void CSVWorld::CommandDelegate::setEditorData (QWidget *editor, const QModelInde // Color columns use a custom editor, so we need explicitly set a data for it CSVWidget::ColorEditor *colorEditor = qobject_cast(editor); - if (colorEditor != NULL) + if (colorEditor != nullptr) { colorEditor->setColor(variant.toInt()); return; diff --git a/apps/openmw/android_main.c b/apps/openmw/android_main.c index d234a369d..9a7b6bc50 100644 --- a/apps/openmw/android_main.c +++ b/apps/openmw/android_main.c @@ -20,14 +20,14 @@ void releaseArgv(); int Java_org_libsdl_app_SDLActivity_getMouseX(JNIEnv *env, jclass cls, jobject obj) { int ret = 0; - SDL_GetMouseState(&ret, NULL); + SDL_GetMouseState(&ret, nullptr); return ret; } int Java_org_libsdl_app_SDLActivity_getMouseY(JNIEnv *env, jclass cls, jobject obj) { int ret = 0; - SDL_GetMouseState(NULL, &ret); + SDL_GetMouseState(nullptr, &ret); return ret; } diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 057113ba3..f5b4171d4 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -197,9 +197,9 @@ bool OMW::Engine::frame(float frametime) } OMW::Engine::Engine(Files::ConfigurationManager& configurationManager) - : mWindow(NULL) + : mWindow(nullptr) , mEncoding(ToUTF8::WINDOWS_1252) - , mEncoder(NULL) + , mEncoder(nullptr) , mScreenCaptureOperation(nullptr) , mSkipMenu (false) , mUseSound (true) @@ -237,18 +237,18 @@ OMW::Engine::~Engine() mEnvironment.cleanup(); delete mScriptContext; - mScriptContext = NULL; + mScriptContext = nullptr; - mWorkQueue = NULL; + mWorkQueue = nullptr; mResourceSystem.reset(); - mViewer = NULL; + mViewer = nullptr; if (mWindow) { SDL_DestroyWindow(mWindow); - mWindow = NULL; + mWindow = nullptr; } SDL_Quit(); diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index efe460b00..e1784d593 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -74,7 +74,7 @@ namespace MWClass if (ref->mBase->mFlags & ESM::Container::Respawn) { MWBase::Environment::get().getWorld()->removeContainerScripts(ptr); - ptr.getRefData().setCustomData(NULL); + ptr.getRefData().setCustomData(nullptr); } } diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 6999558ae..6d0b42bfe 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -298,7 +298,7 @@ namespace MWClass bool healthdmg = true; if (!weapon.isEmpty()) { - const unsigned char *attack = NULL; + const unsigned char *attack = nullptr; if(type == ESM::Weapon::AT_Chop) attack = weapon.get()->mBase->mData.mChop; else if(type == ESM::Weapon::AT_Slash) @@ -828,7 +828,7 @@ namespace MWClass ptr.getRefData().setCount(1); MWBase::Environment::get().getWorld()->removeContainerScripts(ptr); - ptr.getRefData().setCustomData(NULL); + ptr.getRefData().setCustomData(nullptr); // Reset to original position MWBase::Environment::get().getWorld()->moveObject(ptr, ptr.getCellRef().getPosition().pos[0], diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index 3ee228013..ea0abd6f6 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -41,7 +41,7 @@ namespace MWClass { MWWorld::LiveCellRef *ref = ptr.get(); - assert (ref->mBase != NULL); + assert (ref->mBase != nullptr); // TODO: add option somewhere to enable collision for placeable objects if (!model.empty() && (ref->mBase->mData.mFlags & ESM::Light::Carry) == 0) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 4979db2fd..1daf28542 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -610,7 +610,7 @@ namespace MWClass float damage = 0.0f; if(!weapon.isEmpty()) { - const unsigned char *attack = NULL; + const unsigned char *attack = nullptr; if(type == ESM::Weapon::AT_Chop) attack = weapon.get()->mBase->mData.mChop; else if(type == ESM::Weapon::AT_Slash) @@ -1382,7 +1382,7 @@ namespace MWClass ptr.getRefData().setCount(1); MWBase::Environment::get().getWorld()->removeContainerScripts(ptr); - ptr.getRefData().setCustomData(NULL); + ptr.getRefData().setCustomData(nullptr); // Reset to original position MWBase::Environment::get().getWorld()->moveObject(ptr, ptr.getCellRef().getPosition().pos[0], diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index dcc33d1c3..17f69d69b 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -572,7 +572,7 @@ namespace MWDialogue const MWMechanics::CreatureStats& creatureStats = actor.getClass().getCreatureStats(actor); Filter filter(actor, 0, creatureStats.hasTalkedToPlayer()); const ESM::DialInfo *info = filter.search(*dial, false); - if(info != NULL) + if(info != nullptr) { MWBase::WindowManager *winMgr = MWBase::Environment::get().getWindowManager(); if(winMgr->getSubtitlesEnabled()) diff --git a/apps/openmw/mwdialogue/filter.cpp b/apps/openmw/mwdialogue/filter.cpp index 15020898f..042ccb019 100644 --- a/apps/openmw/mwdialogue/filter.cpp +++ b/apps/openmw/mwdialogue/filter.cpp @@ -621,7 +621,7 @@ const ESM::DialInfo* MWDialogue::Filter::search (const ESM::Dialogue& dialogue, std::vector suitableInfos = list (dialogue, fallbackToInfoRefusal, false); if (suitableInfos.empty()) - return NULL; + return nullptr; else return suitableInfos[0]; } diff --git a/apps/openmw/mwdialogue/journalentry.cpp b/apps/openmw/mwdialogue/journalentry.cpp index 9f74d0733..5eab6d5ca 100644 --- a/apps/openmw/mwdialogue/journalentry.cpp +++ b/apps/openmw/mwdialogue/journalentry.cpp @@ -30,7 +30,7 @@ namespace MWDialogue { if (actor.isEmpty()) { - MWScript::InterpreterContext interpreterContext(NULL,MWWorld::Ptr()); + MWScript::InterpreterContext interpreterContext(nullptr, MWWorld::Ptr()); mText = Interpreter::fixDefinesDialog(iter->mResponse, interpreterContext); } else diff --git a/apps/openmw/mwgui/alchemywindow.cpp b/apps/openmw/mwgui/alchemywindow.cpp index a599e6c7b..fa8a96185 100644 --- a/apps/openmw/mwgui/alchemywindow.cpp +++ b/apps/openmw/mwgui/alchemywindow.cpp @@ -30,7 +30,7 @@ namespace MWGui AlchemyWindow::AlchemyWindow() : WindowBase("openmw_alchemy_window.layout") - , mSortModel(NULL) + , mSortModel(nullptr) , mAlchemy(new MWMechanics::Alchemy()) , mApparatus (4) , mIngredients (4) diff --git a/apps/openmw/mwgui/backgroundimage.cpp b/apps/openmw/mwgui/backgroundimage.cpp index 98828a041..55c825ebb 100644 --- a/apps/openmw/mwgui/backgroundimage.cpp +++ b/apps/openmw/mwgui/backgroundimage.cpp @@ -10,7 +10,7 @@ void BackgroundImage::setBackgroundImage (const std::string& image, bool fixedRa if (mChild) { MyGUI::Gui::getInstance().destroyWidget(mChild); - mChild = NULL; + mChild = nullptr; } if (!stretch) { diff --git a/apps/openmw/mwgui/backgroundimage.hpp b/apps/openmw/mwgui/backgroundimage.hpp index 8c963b762..3db5bab16 100644 --- a/apps/openmw/mwgui/backgroundimage.hpp +++ b/apps/openmw/mwgui/backgroundimage.hpp @@ -14,7 +14,7 @@ namespace MWGui MYGUI_RTTI_DERIVED(BackgroundImage) public: - BackgroundImage() : mChild(NULL), mAspect(0) {} + BackgroundImage() : mChild(nullptr), mAspect(0) {} /** * @param fixedRatio Use a fixed ratio of 4:3, regardless of the image dimensions diff --git a/apps/openmw/mwgui/bookpage.cpp b/apps/openmw/mwgui/bookpage.cpp index c3b8935e8..29dfe7f3a 100644 --- a/apps/openmw/mwgui/bookpage.cpp +++ b/apps/openmw/mwgui/bookpage.cpp @@ -110,7 +110,7 @@ struct TypesetBookImpl : TypesetBook Contents::iterator i = mContents.insert (mContents.end (), Content (text.first, text.second)); if (i->empty()) - return Range (Utf8Point (NULL), Utf8Point (NULL)); + return Range (Utf8Point (nullptr), Utf8Point (nullptr)); Utf8Point begin = &i->front (); Utf8Point end = &i->front () + i->size (); @@ -148,7 +148,7 @@ struct TypesetBookImpl : TypesetBook template void visitRuns (int top, int bottom, Visitor const & visitor) const { - visitRuns (top, bottom, NULL, visitor); + visitRuns (top, bottom, nullptr, visitor); } /// hit test with a margin for error. only hits on interactive text fragments are reported. @@ -176,7 +176,7 @@ struct TypesetBookImpl : TypesetBook return hit; } } - return NULL; + return nullptr; } StyleImpl * hitTest (int left, int top) const @@ -213,7 +213,7 @@ struct TypesetBookImpl : TypesetBook for (Styles::iterator i = mStyles.begin (); i != mStyles.end (); ++i) if (&*i == style) return i->mFont; - return NULL; + return nullptr; } struct Typesetter; @@ -253,8 +253,8 @@ struct TypesetBookImpl::Typesetter : BookTypesetter Typesetter (size_t width, size_t height) : mPageWidth (width), mPageHeight(height), - mSection (NULL), mLine (NULL), mRun (NULL), - mCurrentContent (NULL), + mSection (nullptr), mLine (nullptr), mRun (nullptr), + mCurrentContent (nullptr), mCurrentAlignment (AlignLeft) { mBook = std::make_shared (); @@ -342,7 +342,7 @@ struct TypesetBookImpl::Typesetter : BookTypesetter void write (Style * style, size_t begin, size_t end) { - assert (mCurrentContent != NULL); + assert (mCurrentContent != nullptr); assert (end <= mCurrentContent->size ()); assert (begin <= mCurrentContent->size ()); @@ -358,8 +358,8 @@ struct TypesetBookImpl::Typesetter : BookTypesetter add_partial_text(); - mRun = NULL; - mLine = NULL; + mRun = nullptr; + mLine = nullptr; } void sectionBreak (int margin) @@ -368,9 +368,9 @@ struct TypesetBookImpl::Typesetter : BookTypesetter if (mBook->mSections.size () > 0) { - mRun = NULL; - mLine = NULL; - mSection = NULL; + mRun = nullptr; + mLine = nullptr; + mSection = nullptr; if (mBook->mRect.bottom < (mBook->mSections.back ().mRect.bottom + margin)) mBook->mRect.bottom = (mBook->mSections.back ().mRect.bottom + margin); @@ -381,7 +381,7 @@ struct TypesetBookImpl::Typesetter : BookTypesetter { add_partial_text(); - if (mSection != NULL) + if (mSection != nullptr) mSectionAlignment.back () = sectionAlignment; mCurrentAlignment = sectionAlignment; } @@ -491,7 +491,7 @@ struct TypesetBookImpl::Typesetter : BookTypesetter { add_partial_text(); stream.consume (); - mLine = NULL, mRun = NULL; + mLine = nullptr, mRun = nullptr; continue; } @@ -551,7 +551,7 @@ struct TypesetBookImpl::Typesetter : BookTypesetter if (left + space_width + word_width > mPageWidth) { - mLine = NULL, mRun = NULL, left = 0; + mLine = nullptr, mRun = nullptr, left = 0; } else { @@ -580,7 +580,7 @@ struct TypesetBookImpl::Typesetter : BookTypesetter void append_run (StyleImpl * style, Utf8Stream::Point begin, Utf8Stream::Point end, int pc, int right, int bottom) { - if (mSection == NULL) + if (mSection == nullptr) { mBook->mSections.push_back (Section ()); mSection = &mBook->mSections.back (); @@ -588,7 +588,7 @@ struct TypesetBookImpl::Typesetter : BookTypesetter mSectionAlignment.push_back (mCurrentAlignment); } - if (mLine == NULL) + if (mLine == nullptr) { mSection->mLines.push_back (Line ()); mLine = &mSection->mLines.back (); @@ -613,7 +613,7 @@ struct TypesetBookImpl::Typesetter : BookTypesetter if (mLine->mRect.bottom < bottom) mLine->mRect.bottom = bottom; - if (mRun == NULL || mRun->mStyle != style || mRun->mRange.second != begin) + if (mRun == nullptr || mRun->mStyle != style || mRun->mRange.second != begin) { int left = mRun ? mRun->mRight : mLine->mRect.left; @@ -843,17 +843,17 @@ protected: TextFormat (MyGUI::IFont* id, PageDisplay * display) : mFont (id), mCountVertex (0), - mTexture (NULL), - mRenderItem (NULL), + mTexture (nullptr), + mRenderItem (nullptr), mDisplay (display) { } void createDrawItem (MyGUI::ILayerNode* node) { - assert (mRenderItem == NULL); + assert (mRenderItem == nullptr); - if (mTexture != NULL) + if (mTexture != nullptr) { mRenderItem = node->addToRenderItem(mTexture, false, false); mRenderItem->addDrawItem(this, mCountVertex); @@ -862,12 +862,12 @@ protected: void destroyDrawItem (MyGUI::ILayerNode* node) { - assert (mTexture != NULL ? mRenderItem != NULL : mRenderItem == NULL); + assert (mTexture != nullptr ? mRenderItem != nullptr : mRenderItem == nullptr); - if (mTexture != NULL) + if (mTexture != nullptr) { mRenderItem->removeDrawItem (this); - mRenderItem = NULL; + mRenderItem = nullptr; } } @@ -920,9 +920,9 @@ public: resetPage (); mViewTop = 0; mViewBottom = 0; - mFocusItem = NULL; + mFocusItem = nullptr; mItemActive = false; - mNode = NULL; + mNode = nullptr; } void dirtyFocusItem () @@ -1053,7 +1053,7 @@ public: for (ActiveTextFormats::iterator i = mActiveTextFormats.begin (); i != mActiveTextFormats.end (); ++i) { - if (mNode != NULL) + if (mNode != nullptr) i->second->destroyDrawItem (mNode); i->second.reset(); } @@ -1089,7 +1089,7 @@ public: else if (mBook && isPageDifferent (newPage)) { - if (mNode != NULL) + if (mNode != nullptr) for (ActiveTextFormats::iterator i = mActiveTextFormats.begin (); i != mActiveTextFormats.end (); ++i) mNode->outOfDate(i->second->mRenderItem); @@ -1137,7 +1137,7 @@ public: { newBook->visitRuns (0, 0x7FFFFFFF, CreateActiveFormat (this)); - if (mNode != NULL) + if (mNode != nullptr) for (ActiveTextFormats::iterator i = mActiveTextFormats.begin (); i != mActiveTextFormats.end (); ++i) i->second->createDrawItem (mNode); } @@ -1238,7 +1238,7 @@ public: { _checkMargin(); - if (mNode != NULL) + if (mNode != nullptr) for (ActiveTextFormats::iterator i = mActiveTextFormats.begin (); i != mActiveTextFormats.end (); ++i) mNode->outOfDate (i->second->mRenderItem); } @@ -1247,7 +1247,7 @@ public: { _checkMargin (); - if (mNode != NULL) + if (mNode != nullptr) for (ActiveTextFormats::iterator i = mActiveTextFormats.begin (); i != mActiveTextFormats.end (); ++i) mNode->outOfDate (i->second->mRenderItem); @@ -1258,7 +1258,7 @@ public: for (ActiveTextFormats::iterator i = mActiveTextFormats.begin (); i != mActiveTextFormats.end (); ++i) i->second->destroyDrawItem (mNode); - mNode = NULL; + mNode = nullptr; } }; @@ -1269,7 +1269,7 @@ MYGUI_RTTI_DERIVED(BookPage) public: BookPageImpl() - : mPageDisplay(NULL) + : mPageDisplay(nullptr) { } diff --git a/apps/openmw/mwgui/class.cpp b/apps/openmw/mwgui/class.cpp index 6ed7a4491..45abe889e 100644 --- a/apps/openmw/mwgui/class.cpp +++ b/apps/openmw/mwgui/class.cpp @@ -399,12 +399,12 @@ namespace MWGui CreateClassDialog::CreateClassDialog() : WindowModal("openmw_chargen_create_class.layout") - , mSpecDialog(NULL) - , mAttribDialog(NULL) - , mSkillDialog(NULL) - , mDescDialog(NULL) - , mAffectedAttribute(NULL) - , mAffectedSkill(NULL) + , mSpecDialog(nullptr) + , mAttribDialog(nullptr) + , mSkillDialog(nullptr) + , mDescDialog(nullptr) + , mAffectedAttribute(nullptr) + , mAffectedSkill(nullptr) { // Centre dialog center(); diff --git a/apps/openmw/mwgui/companionwindow.cpp b/apps/openmw/mwgui/companionwindow.cpp index c6ef2890d..b2639e938 100644 --- a/apps/openmw/mwgui/companionwindow.cpp +++ b/apps/openmw/mwgui/companionwindow.cpp @@ -37,8 +37,8 @@ namespace MWGui CompanionWindow::CompanionWindow(DragAndDrop *dragAndDrop, MessageBoxManager* manager) : WindowBase("openmw_companion_window.layout") - , mSortModel(NULL) - , mModel(NULL) + , mSortModel(nullptr) + , mModel(nullptr) , mSelectedItem(-1) , mDragAndDrop(dragAndDrop) , mMessageBoxManager(manager) @@ -89,7 +89,7 @@ void CompanionWindow::onItemSelected(int index) dialog->eventOkClicked += MyGUI::newDelegate(this, &CompanionWindow::dragItem); } else - dragItem (NULL, count); + dragItem (nullptr, count); } void CompanionWindow::dragItem(MyGUI::Widget* sender, int count) @@ -179,9 +179,9 @@ void CompanionWindow::onReferenceUnavailable() void CompanionWindow::resetReference() { ReferenceInterface::resetReference(); - mItemView->setModel(NULL); - mModel = NULL; - mSortModel = NULL; + mItemView->setModel(nullptr); + mModel = nullptr; + mSortModel = nullptr; } diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 99d0ea0d1..2f9643f74 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -31,8 +31,8 @@ namespace MWGui ContainerWindow::ContainerWindow(DragAndDrop* dragAndDrop) : WindowBase("openmw_container_window.layout") , mDragAndDrop(dragAndDrop) - , mSortModel(NULL) - , mModel(NULL) + , mSortModel(nullptr) + , mModel(nullptr) , mSelectedItem(-1) { getWidget(mDisposeCorpseButton, "DisposeCorpseButton"); @@ -83,7 +83,7 @@ namespace MWGui dialog->eventOkClicked += MyGUI::newDelegate(this, &ContainerWindow::dragItem); } else - dragItem (NULL, count); + dragItem (nullptr, count); } void ContainerWindow::dragItem(MyGUI::Widget* sender, int count) @@ -151,9 +151,9 @@ namespace MWGui void ContainerWindow::resetReference() { ReferenceInterface::resetReference(); - mItemView->setModel(NULL); - mModel = NULL; - mSortModel = NULL; + mItemView->setModel(nullptr); + mModel = nullptr; + mSortModel = nullptr; } void ContainerWindow::onClose() @@ -171,7 +171,7 @@ namespace MWGui void ContainerWindow::onTakeAllButtonClicked(MyGUI::Widget* _sender) { - if(mDragAndDrop != NULL && mDragAndDrop->mIsOnDragAndDrop) + if(mDragAndDrop != nullptr && mDragAndDrop->mIsOnDragAndDrop) return; MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCloseButton); @@ -219,7 +219,7 @@ namespace MWGui void ContainerWindow::onDisposeCorpseButtonClicked(MyGUI::Widget *sender) { - if(mDragAndDrop == NULL || !mDragAndDrop->mIsOnDragAndDrop) + if(mDragAndDrop == nullptr || !mDragAndDrop->mIsOnDragAndDrop) { MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCloseButton); diff --git a/apps/openmw/mwgui/countdialog.cpp b/apps/openmw/mwgui/countdialog.cpp index 4292f2e04..baf3a43ab 100644 --- a/apps/openmw/mwgui/countdialog.cpp +++ b/apps/openmw/mwgui/countdialog.cpp @@ -63,7 +63,7 @@ namespace MWGui void CountDialog::onOkButtonClicked(MyGUI::Widget* _sender) { - eventOkClicked(NULL, mSlider->getScrollPosition()+1); + eventOkClicked(nullptr, mSlider->getScrollPosition()+1); setVisible(false); } @@ -72,7 +72,7 @@ namespace MWGui // Enter key void CountDialog::onEnterKeyPressed(MyGUI::EditBox* _sender) { - eventOkClicked(NULL, mSlider->getScrollPosition()+1); + eventOkClicked(nullptr, mSlider->getScrollPosition()+1); setVisible(false); // To do not spam onEnterKeyPressed() again and again diff --git a/apps/openmw/mwgui/cursor.cpp b/apps/openmw/mwgui/cursor.cpp index 82e0b9699..ed8a76eb8 100644 --- a/apps/openmw/mwgui/cursor.cpp +++ b/apps/openmw/mwgui/cursor.cpp @@ -10,7 +10,7 @@ namespace MWGui ResourceImageSetPointerFix::ResourceImageSetPointerFix() - : mImageSet(NULL) + : mImageSet(nullptr) , mRotation(0) { } @@ -47,7 +47,7 @@ namespace MWGui void ResourceImageSetPointerFix::setImage(MyGUI::ImageBox* _image) { - if (mImageSet != NULL) + if (mImageSet != nullptr) _image->setItemResourceInfo(mImageSet->getIndexInfo(0, 0)); } diff --git a/apps/openmw/mwgui/draganddrop.cpp b/apps/openmw/mwgui/draganddrop.cpp index d81b2ed00..daf9f6636 100644 --- a/apps/openmw/mwgui/draganddrop.cpp +++ b/apps/openmw/mwgui/draganddrop.cpp @@ -20,10 +20,10 @@ namespace MWGui DragAndDrop::DragAndDrop() : mIsOnDragAndDrop(false) - , mDraggedWidget(NULL) - , mSourceModel(NULL) - , mSourceView(NULL) - , mSourceSortModel(NULL) + , mDraggedWidget(nullptr) + , mSourceModel(nullptr) + , mSourceView(nullptr) + , mSourceSortModel(nullptr) , mDraggedCount(0) { } diff --git a/apps/openmw/mwgui/enchantingdialog.cpp b/apps/openmw/mwgui/enchantingdialog.cpp index 039377dee..98980e339 100644 --- a/apps/openmw/mwgui/enchantingdialog.cpp +++ b/apps/openmw/mwgui/enchantingdialog.cpp @@ -28,7 +28,7 @@ namespace MWGui EnchantingDialog::EnchantingDialog() : WindowBase("openmw_enchanting_dialog.layout") , EffectEditorBase(EffectEditorBase::Enchanting) - , mItemSelectionDialog(NULL) + , mItemSelectionDialog(nullptr) { getWidget(mName, "NameEdit"); getWidget(mCancelButton, "CancelButton"); diff --git a/apps/openmw/mwgui/exposedwindow.cpp b/apps/openmw/mwgui/exposedwindow.cpp index 150a8c893..1a0484e72 100644 --- a/apps/openmw/mwgui/exposedwindow.cpp +++ b/apps/openmw/mwgui/exposedwindow.cpp @@ -14,7 +14,7 @@ namespace MWGui if (widgets.empty()) { MYGUI_ASSERT( ! _throw, "widget name '" << _name << "' not found in skin of layout '" << getName() << "'"); - return NULL; + return nullptr; } else { diff --git a/apps/openmw/mwgui/formatting.cpp b/apps/openmw/mwgui/formatting.cpp index fd5ed4faa..78359608e 100644 --- a/apps/openmw/mwgui/formatting.cpp +++ b/apps/openmw/mwgui/formatting.cpp @@ -27,7 +27,7 @@ namespace MWGui BookTextParser::BookTextParser(const std::string & text) : mIndex(0), mText(text), mIgnoreNewlineTags(true), mIgnoreLineEndings(true), mClosingTag(false) { - MWScript::InterpreterContext interpreterContext(NULL, MWWorld::Ptr()); // empty arguments, because there is no locals or actor + MWScript::InterpreterContext interpreterContext(nullptr, MWWorld::Ptr()); // empty arguments, because there is no locals or actor mText = Interpreter::fixDefinesBook(mText, interpreterContext); boost::algorithm::replace_all(mText, "\r", ""); diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index 91467d490..6076c1c10 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -70,20 +70,20 @@ namespace MWGui HUD::HUD(CustomMarkerCollection &customMarkers, DragAndDrop* dragAndDrop, MWRender::LocalMap* localMapRender) : WindowBase("openmw_hud.layout") , LocalMapBase(customMarkers, localMapRender, Settings::Manager::getBool("local map hud fog of war", "Map")) - , mHealth(NULL) - , mMagicka(NULL) - , mStamina(NULL) - , mDrowning(NULL) - , mWeapImage(NULL) - , mSpellImage(NULL) - , mWeapStatus(NULL) - , mSpellStatus(NULL) - , mEffectBox(NULL) - , mMinimap(NULL) - , mCrosshair(NULL) - , mCellNameBox(NULL) - , mDrowningFrame(NULL) - , mDrowningFlash(NULL) + , mHealth(nullptr) + , mMagicka(nullptr) + , mStamina(nullptr) + , mDrowning(nullptr) + , mWeapImage(nullptr) + , mSpellImage(nullptr) + , mWeapStatus(nullptr) + , mSpellStatus(nullptr) + , mEffectBox(nullptr) + , mMinimap(nullptr) + , mCrosshair(nullptr) + , mCellNameBox(nullptr) + , mDrowningFrame(nullptr) + , mDrowningFlash(nullptr) , mHealthManaStaminaBaseLeft(0) , mWeapBoxBaseLeft(0) , mSpellBoxBaseLeft(0) @@ -247,7 +247,7 @@ namespace MWGui float mouseY = cursorPosition.top / float(viewSize.height); WorldItemModel drop (mouseX, mouseY); - mDragAndDrop->drop(&drop, NULL); + mDragAndDrop->drop(&drop, nullptr); MWBase::Environment::get().getWindowManager()->changePointer("arrow"); } diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 0d37bbff3..1877ef97d 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -60,8 +60,8 @@ namespace MWGui : WindowPinnableBase("openmw_inventory_window.layout") , mDragAndDrop(dragAndDrop) , mSelectedItem(-1) - , mSortModel(NULL) - , mTradeModel(NULL) + , mSortModel(nullptr) + , mTradeModel(nullptr) , mGuiMode(GM_Inventory) , mLastXSize(0) , mLastYSize(0) @@ -157,9 +157,9 @@ namespace MWGui void InventoryWindow::clear() { mPtr = MWWorld::Ptr(); - mTradeModel = NULL; - mSortModel = NULL; - mItemView->setModel(NULL); + mTradeModel = nullptr; + mSortModel = nullptr; + mItemView->setModel(nullptr); } void InventoryWindow::setGuiMode(GuiMode mode) @@ -296,9 +296,9 @@ namespace MWGui { mSelectedItem = index; if (mTrading) - sellItem (NULL, count); + sellItem (nullptr, count); else - dragItem (NULL, count); + dragItem (nullptr, count); } } diff --git a/apps/openmw/mwgui/itemchargeview.cpp b/apps/openmw/mwgui/itemchargeview.cpp index 3001b9ef2..10c36c73f 100644 --- a/apps/openmw/mwgui/itemchargeview.cpp +++ b/apps/openmw/mwgui/itemchargeview.cpp @@ -21,7 +21,7 @@ namespace MWGui { ItemChargeView::ItemChargeView() - : mScrollView(NULL), + : mScrollView(nullptr), mDisplayMode(DisplayMode_Health) { } @@ -36,7 +36,7 @@ namespace MWGui Base::initialiseOverride(); assignWidget(mScrollView, "ScrollView"); - if (mScrollView == NULL) + if (mScrollView == nullptr) throw std::runtime_error("Item charge view needs a scroll view"); mScrollView->setCanvasAlign(MyGUI::Align::Left | MyGUI::Align::Top); diff --git a/apps/openmw/mwgui/itemmodel.cpp b/apps/openmw/mwgui/itemmodel.cpp index e3f38a54c..c7c10e8e4 100644 --- a/apps/openmw/mwgui/itemmodel.cpp +++ b/apps/openmw/mwgui/itemmodel.cpp @@ -31,7 +31,7 @@ namespace MWGui ItemStack::ItemStack() : mType(Type_Normal) , mFlags(0) - , mCreator(NULL) + , mCreator(nullptr) , mCount(0) { } @@ -106,7 +106,7 @@ namespace MWGui ProxyItemModel::ProxyItemModel() - : mSourceModel(NULL) + : mSourceModel(nullptr) { } @@ -167,7 +167,7 @@ namespace MWGui if (mSourceModel) { delete mSourceModel; - mSourceModel = NULL; + mSourceModel = nullptr; } mSourceModel = sourceModel; diff --git a/apps/openmw/mwgui/itemselection.cpp b/apps/openmw/mwgui/itemselection.cpp index effd11d55..23f1398ea 100644 --- a/apps/openmw/mwgui/itemselection.cpp +++ b/apps/openmw/mwgui/itemselection.cpp @@ -12,8 +12,8 @@ namespace MWGui ItemSelectionDialog::ItemSelectionDialog(const std::string &label) : WindowModal("openmw_itemselection_dialog.layout") - , mSortModel(NULL) - , mModel(NULL) + , mSortModel(nullptr) + , mModel(nullptr) { getWidget(mItemView, "ItemView"); mItemView->eventItemClicked += MyGUI::newDelegate(this, &ItemSelectionDialog::onSelectedItem); diff --git a/apps/openmw/mwgui/itemview.cpp b/apps/openmw/mwgui/itemview.cpp index 2cdfcada4..94dcc77c5 100644 --- a/apps/openmw/mwgui/itemview.cpp +++ b/apps/openmw/mwgui/itemview.cpp @@ -18,8 +18,8 @@ namespace MWGui { ItemView::ItemView() - : mModel(NULL) - , mScrollView(NULL) + : mModel(nullptr) + , mScrollView(nullptr) { } @@ -44,7 +44,7 @@ void ItemView::initialiseOverride() Base::initialiseOverride(); assignWidget(mScrollView, "ScrollView"); - if (mScrollView == NULL) + if (mScrollView == nullptr) throw std::runtime_error("Item view needs a scroll view"); mScrollView->setCanvasAlign(MyGUI::Align::Left | MyGUI::Align::Top); diff --git a/apps/openmw/mwgui/itemwidget.cpp b/apps/openmw/mwgui/itemwidget.cpp index 6b5be0314..223bced4f 100644 --- a/apps/openmw/mwgui/itemwidget.cpp +++ b/apps/openmw/mwgui/itemwidget.cpp @@ -32,10 +32,10 @@ namespace MWGui { ItemWidget::ItemWidget() - : mItem(NULL) - , mItemShadow(NULL) - , mFrame(NULL) - , mText(NULL) + : mItem(nullptr) + , mItemShadow(nullptr) + , mFrame(nullptr) + , mText(nullptr) { } diff --git a/apps/openmw/mwgui/journalviewmodel.cpp b/apps/openmw/mwgui/journalviewmodel.cpp index 63b48eab1..3621656bc 100644 --- a/apps/openmw/mwgui/journalviewmodel.cpp +++ b/apps/openmw/mwgui/journalviewmodel.cpp @@ -39,7 +39,7 @@ struct JournalViewModelImpl : JournalViewModel static Utf8Span toUtf8Span (std::string const & str) { if (str.size () == 0) - return Utf8Span (Utf8Point (NULL), Utf8Point (NULL)); + return Utf8Span (Utf8Point (nullptr), Utf8Point (nullptr)); Utf8Point point = reinterpret_cast (str.c_str ()); diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp index be3d477e1..38ab70d54 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -140,7 +140,7 @@ namespace MWGui // Callback removes itself when done if (renderInfo.getCurrentCamera()) - renderInfo.getCurrentCamera()->setInitialDrawCallback(NULL); + renderInfo.getCurrentCamera()->setInitialDrawCallback(nullptr); } private: @@ -208,7 +208,7 @@ namespace MWGui else mImportantLabel = false; // label was already shown on loading screen - mViewer->getSceneData()->setComputeBoundingSphereCallback(NULL); + mViewer->getSceneData()->setComputeBoundingSphereCallback(nullptr); mViewer->getSceneData()->dirtyBound(); //std::cout << "loading took " << mTimer.time_m() - mLoadingOnTime << std::endl; diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index 5017b8893..1b9e024ba 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -25,10 +25,10 @@ namespace MWGui : WindowBase("openmw_mainmenu.layout") , mWidth (w), mHeight (h) , mVFS(vfs), mButtonBox(0) - , mBackground(NULL) - , mVideoBackground(NULL) - , mVideo(NULL) - , mSaveGameDialog(NULL) + , mBackground(nullptr) + , mVideoBackground(nullptr) + , mVideo(nullptr) + , mSaveGameDialog(nullptr) { getWidget(mVersionText, "VersionText"); mVersionText->setCaption(versionDescription); @@ -147,13 +147,13 @@ namespace MWGui if (mVideo && !show) { MyGUI::Gui::getInstance().destroyWidget(mVideoBackground); - mVideoBackground = NULL; - mVideo = NULL; + mVideoBackground = nullptr; + mVideo = nullptr; } if (mBackground && !show) { MyGUI::Gui::getInstance().destroyWidget(mBackground); - mBackground = NULL; + mBackground = nullptr; } if (!show) diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index 2c8ad0565..b9b59965d 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -161,8 +161,8 @@ namespace MWGui , mCurX(0) , mCurY(0) , mInterior(false) - , mLocalMap(NULL) - , mCompass(NULL) + , mLocalMap(nullptr) + , mCompass(nullptr) , mChanged(true) , mFogOfWarToggled(true) , mFogOfWarEnabled(fogOfWarEnabled) @@ -388,7 +388,7 @@ namespace MWGui box->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 0.f, 1.f, 1.f)); } else - box->setRenderItemTexture(NULL); + box->setRenderItemTexture(nullptr); } } mMapTextures.swap(textures); @@ -598,7 +598,7 @@ namespace MWGui addDetectionMarkers(MWBase::World::Detect_Enchantment); // Add marker for the spot marked with Mark magic effect - MWWorld::CellStore* markedCell = NULL; + MWWorld::CellStore* markedCell = nullptr; ESM::Position markedPosition; MWBase::Environment::get().getWorld()->getPlayer().getMarkedPosition(markedCell, markedPosition); if (markedCell && markedCell->isExterior() == !mInterior @@ -627,11 +627,11 @@ namespace MWGui , LocalMapBase(customMarkers, localMapRender) , NoDrop(drag, mMainWidget) , mGlobalMap(0) - , mGlobalMapImage(NULL) - , mGlobalMapOverlay(NULL) + , mGlobalMapImage(nullptr) + , mGlobalMapOverlay(nullptr) , mGlobal(Settings::Manager::getBool("global", "Map")) - , mEventBoxGlobal(NULL) - , mEventBoxLocal(NULL) + , mEventBoxGlobal(nullptr) + , mEventBoxLocal(nullptr) , mGlobalMapRender(new MWRender::GlobalMap(localMapRender->getRoot(), workQueue)) , mEditNoteDialog() { @@ -872,7 +872,7 @@ namespace MWGui if (!destNotes.empty()) { - MarkerUserData data (NULL); + MarkerUserData data (nullptr); data.notes = destNotes; data.caption = markerWidget->getUserString("Caption_TextOneLine"); markerWidget->setUserData(data); diff --git a/apps/openmw/mwgui/messagebox.cpp b/apps/openmw/mwgui/messagebox.cpp index 2fbce97d4..e83c4b238 100644 --- a/apps/openmw/mwgui/messagebox.cpp +++ b/apps/openmw/mwgui/messagebox.cpp @@ -20,8 +20,8 @@ namespace MWGui MessageBoxManager::MessageBoxManager (float timePerChar) { - mInterMessageBoxe = NULL; - mStaticMessageBox = NULL; + mInterMessageBoxe = nullptr; + mStaticMessageBox = nullptr; mLastButtonPressed = -1; mMessageBoxSpeed = timePerChar; } @@ -42,14 +42,14 @@ namespace MWGui mInterMessageBoxe->setVisible(false); delete mInterMessageBoxe; - mInterMessageBoxe = NULL; + mInterMessageBoxe = nullptr; } std::vector::iterator it(mMessageBoxes.begin()); for (; it != mMessageBoxes.end(); ++it) { if (*it == mStaticMessageBox) - mStaticMessageBox = NULL; + mStaticMessageBox = nullptr; delete *it; } mMessageBoxes.clear(); @@ -81,11 +81,11 @@ namespace MWGui ++it; } - if(mInterMessageBoxe != NULL && mInterMessageBoxe->mMarkedToDelete) { + if(mInterMessageBoxe != nullptr && mInterMessageBoxe->mMarkedToDelete) { mLastButtonPressed = mInterMessageBoxe->readPressedButton(); mInterMessageBoxe->setVisible(false); delete mInterMessageBoxe; - mInterMessageBoxe = NULL; + mInterMessageBoxe = nullptr; MWBase::Environment::get().getInputManager()->changeInputMode( MWBase::Environment::get().getWindowManager()->isGuiMode()); } @@ -119,17 +119,17 @@ namespace MWGui void MessageBoxManager::removeStaticMessageBox () { removeMessageBox(mStaticMessageBox); - mStaticMessageBox = NULL; + mStaticMessageBox = nullptr; } bool MessageBoxManager::createInteractiveMessageBox (const std::string& message, const std::vector& buttons) { - if (mInterMessageBoxe != NULL) + if (mInterMessageBoxe != nullptr) { Log(Debug::Warning) << "Warning: replacing an interactive message box that was not answered yet"; mInterMessageBoxe->setVisible(false); delete mInterMessageBoxe; - mInterMessageBoxe = NULL; + mInterMessageBoxe = nullptr; } mInterMessageBoxe = new InteractiveMessageBox(*this, message, buttons); @@ -140,7 +140,7 @@ namespace MWGui bool MessageBoxManager::isInteractiveMessageBox () { - return mInterMessageBoxe != NULL; + return mInterMessageBoxe != nullptr; } @@ -373,7 +373,7 @@ namespace MWGui } } } - return NULL; + return nullptr; } void InteractiveMessageBox::mousePressed (MyGUI::Widget* pressed) diff --git a/apps/openmw/mwgui/race.cpp b/apps/openmw/mwgui/race.cpp index ee058e02e..be0dff660 100644 --- a/apps/openmw/mwgui/race.cpp +++ b/apps/openmw/mwgui/race.cpp @@ -129,10 +129,10 @@ namespace MWGui updateSkills(); updateSpellPowers(); - mPreviewImage->setRenderItemTexture(NULL); + mPreviewImage->setRenderItemTexture(nullptr); - mPreview.reset(NULL); - mPreviewTexture.reset(NULL); + mPreview.reset(nullptr); + mPreviewTexture.reset(nullptr); mPreview.reset(new MWRender::RaceSelectionPreview(mParent, mResourceSystem)); mPreview->rebuild(); @@ -190,10 +190,10 @@ namespace MWGui { WindowModal::onClose(); - mPreviewImage->setRenderItemTexture(NULL); + mPreviewImage->setRenderItemTexture(nullptr); - mPreviewTexture.reset(NULL); - mPreview.reset(NULL); + mPreviewTexture.reset(nullptr); + mPreview.reset(nullptr); } // widget controls diff --git a/apps/openmw/mwgui/recharge.cpp b/apps/openmw/mwgui/recharge.cpp index a6ed16bd4..93047956a 100644 --- a/apps/openmw/mwgui/recharge.cpp +++ b/apps/openmw/mwgui/recharge.cpp @@ -31,7 +31,7 @@ namespace MWGui Recharge::Recharge() : WindowBase("openmw_recharge_dialog.layout") - , mItemSelectionDialog(NULL) + , mItemSelectionDialog(nullptr) { getWidget(mBox, "Box"); getWidget(mGemBox, "GemBox"); @@ -90,7 +90,7 @@ void Recharge::updateView() mBox->update(); Gui::Box* box = dynamic_cast(mMainWidget); - if (box == NULL) + if (box == nullptr) throw std::runtime_error("main widget must be a box"); box->notifyChildrenSizeChanged(); diff --git a/apps/openmw/mwgui/repair.cpp b/apps/openmw/mwgui/repair.cpp index 11a8aece2..ea79e0326 100644 --- a/apps/openmw/mwgui/repair.cpp +++ b/apps/openmw/mwgui/repair.cpp @@ -27,7 +27,7 @@ namespace MWGui Repair::Repair() : WindowBase("openmw_repair.layout") - , mItemSelectionDialog(NULL) + , mItemSelectionDialog(nullptr) { getWidget(mRepairBox, "RepairBox"); getWidget(mToolBox, "ToolBox"); @@ -99,7 +99,7 @@ void Repair::updateRepairView() mRepairBox->update(); Gui::Box* box = dynamic_cast(mMainWidget); - if (box == NULL) + if (box == nullptr) throw std::runtime_error("main widget must be a box"); box->notifyChildrenSizeChanged(); diff --git a/apps/openmw/mwgui/review.cpp b/apps/openmw/mwgui/review.cpp index b59b1582c..f2f1cf892 100644 --- a/apps/openmw/mwgui/review.cpp +++ b/apps/openmw/mwgui/review.cpp @@ -379,7 +379,7 @@ namespace MWGui // starting spells std::vector spells; - const ESM::Race* race = NULL; + const ESM::Race* race = nullptr; if (!mRaceId.empty()) race = MWBase::Environment::get().getWorld()->getStore().get().find(mRaceId); diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index 3264e5e33..2027210d7 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -37,8 +37,8 @@ namespace MWGui SaveGameDialog::SaveGameDialog() : WindowModal("openmw_savegame_dialog.layout") , mSaving(true) - , mCurrentCharacter(NULL) - , mCurrentSlot(NULL) + , mCurrentCharacter(nullptr) + , mCurrentSlot(nullptr) { getWidget(mScreenshot, "Screenshot"); getWidget(mCharacterSelection, "SelectCharacter"); @@ -99,7 +99,7 @@ namespace MWGui if (mSaveList->getItemCount() == 0) { size_t previousIndex = mCharacterSelection->getIndexSelected(); - mCurrentCharacter = NULL; + mCurrentCharacter = nullptr; mCharacterSelection->removeItemAt(previousIndex); if (mCharacterSelection->getItemCount()) { @@ -146,8 +146,8 @@ namespace MWGui mCharacterSelection->setCaption(""); mCharacterSelection->removeAllItems(); - mCurrentCharacter = NULL; - mCurrentSlot = NULL; + mCurrentCharacter = nullptr; + mCurrentSlot = nullptr; mSaveList->removeAllItems(); onSlotSelected(mSaveList, MyGUI::ITEM_NONE); @@ -250,12 +250,12 @@ namespace MWGui void SaveGameDialog::accept(bool reallySure) { // Remove for MyGUI 3.2.2 - MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(NULL); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(nullptr); if (mSaving) { // If overwriting an existing slot, ask for confirmation first - if (mCurrentSlot != NULL && !reallySure) + if (mCurrentSlot != nullptr && !reallySure) { ConfirmationDialog* dialog = MWBase::Environment::get().getWindowManager()->getConfirmationDialog(); dialog->askForConfirmation("#{sMessage4}"); @@ -318,7 +318,7 @@ namespace MWGui MWBase::StateManager* mgr = MWBase::Environment::get().getStateManager(); unsigned int i=0; - const MWState::Character* character = NULL; + const MWState::Character* character = nullptr; for (MWBase::StateManager::CharacterIterator it = mgr->characterBegin(); it != mgr->characterEnd(); ++it, ++i) { if (i == pos) @@ -327,7 +327,7 @@ namespace MWGui assert(character && "Can't find selected character"); mCurrentCharacter = character; - mCurrentSlot = NULL; + mCurrentSlot = nullptr; fillSaveList(); } @@ -379,7 +379,7 @@ namespace MWGui if (pos == MyGUI::ITEM_NONE || !mCurrentCharacter) { - mCurrentSlot = NULL; + mCurrentSlot = nullptr; mInfoText->setCaption(""); mScreenshot->setImageTexture(""); return; @@ -388,7 +388,7 @@ namespace MWGui if (mSaving) mSaveNameEdit->setCaption(sender->getItemNameAt(pos)); - mCurrentSlot = NULL; + mCurrentSlot = nullptr; unsigned int i=0; for (MWState::Character::SlotIterator it = mCurrentCharacter->begin(); it != mCurrentCharacter->end(); ++it, ++i) { @@ -404,7 +404,7 @@ namespace MWGui timeinfo = localtime(&time); // Use system/environment locale settings for datetime formatting - char* oldLctime = setlocale(LC_TIME, NULL); + char* oldLctime = setlocale(LC_TIME, nullptr); setlocale(LC_TIME, ""); const int size=1024; diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index 206283133..46d8b38ec 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -55,7 +55,7 @@ namespace MWGui EditEffectDialog::EditEffectDialog() : WindowModal("openmw_edit_effect.layout") , mEditing(false) - , mMagicEffect(NULL) + , mMagicEffect(nullptr) , mConstantEffect(false) { init(mEffect); @@ -481,7 +481,7 @@ namespace MWGui mPriceLabel->setCaption(MyGUI::utility::toString(int(price))); - float chance = MWMechanics::calcSpellBaseSuccessChance(&mSpell, MWMechanics::getPlayer(), NULL); + float chance = MWMechanics::calcSpellBaseSuccessChance(&mSpell, MWMechanics::getPlayer(), nullptr); int intChance = std::min(100, int(chance)); mSuccessChance->setCaption(MyGUI::utility::toString(intChance)); @@ -491,11 +491,11 @@ namespace MWGui EffectEditorBase::EffectEditorBase(Type type) - : mAvailableEffectsList(NULL) - , mUsedEffectsView(NULL) + : mAvailableEffectsList(nullptr) + , mUsedEffectsView(nullptr) , mAddEffectDialog() - , mSelectAttributeDialog(NULL) - , mSelectSkillDialog(NULL) + , mSelectAttributeDialog(nullptr) + , mSelectSkillDialog(nullptr) , mSelectedEffect(0) , mSelectedKnownEffectId(0) , mConstantEffect(false) diff --git a/apps/openmw/mwgui/spellmodel.cpp b/apps/openmw/mwgui/spellmodel.cpp index 0933737ca..6dadebca2 100644 --- a/apps/openmw/mwgui/spellmodel.cpp +++ b/apps/openmw/mwgui/spellmodel.cpp @@ -71,7 +71,7 @@ namespace MWGui { newSpell.mType = Spell::Type_Spell; std::string cost = std::to_string(spell->mData.mCost); - std::string chance = std::to_string(int(MWMechanics::getSpellSuccessChance(spell, mActor, NULL, true, true))); + std::string chance = std::to_string(int(MWMechanics::getSpellSuccessChance(spell, mActor, nullptr, true, true))); newSpell.mCostColumn = cost + "/" + chance; } else diff --git a/apps/openmw/mwgui/spellview.cpp b/apps/openmw/mwgui/spellview.cpp index 702c2f840..879ce471f 100644 --- a/apps/openmw/mwgui/spellview.cpp +++ b/apps/openmw/mwgui/spellview.cpp @@ -24,7 +24,7 @@ namespace MWGui } SpellView::SpellView() - : mScrollView(NULL) + : mScrollView(nullptr) , mShowCostColumn(true) , mHighlightSelected(true) { @@ -35,7 +35,7 @@ namespace MWGui Base::initialiseOverride(); assignWidget(mScrollView, "ScrollView"); - if (mScrollView == NULL) + if (mScrollView == nullptr) throw std::runtime_error("Item view needs a scroll view"); mScrollView->setCanvasAlign(MyGUI::Align::Left | MyGUI::Align::Top); @@ -131,7 +131,7 @@ namespace MWGui mLines.push_back(LineInfo(t, costChance, i)); } else - mLines.push_back(LineInfo(t, (MyGUI::Widget*)NULL, i)); + mLines.push_back(LineInfo(t, (MyGUI::Widget*)nullptr, i)); t->setStateSelected(spell.mSelected); } @@ -177,7 +177,7 @@ namespace MWGui { maxSpellIndexFound = spellIndex; Gui::SharedStateButton* costButton = reinterpret_cast(it->mRightWidget); - if ((costButton != NULL) && (costButton->getCaption() != spell.mCostColumn)) + if ((costButton != nullptr) && (costButton->getCaption() != spell.mCostColumn)) { costButton->setCaption(spell.mCostColumn); } @@ -238,7 +238,7 @@ namespace MWGui MyGUI::IntCoord(0, 0, mScrollView->getWidth(), 18), MyGUI::Align::Left | MyGUI::Align::Top); separator->setNeedMouseFocus(false); - mLines.push_back(LineInfo(separator, (MyGUI::Widget*)NULL, NoSpellIndex)); + mLines.push_back(LineInfo(separator, (MyGUI::Widget*)nullptr, NoSpellIndex)); } MyGUI::TextBox* groupWidget = mScrollView->createWidget("SandBrightText", @@ -260,7 +260,7 @@ namespace MWGui mLines.push_back(LineInfo(groupWidget, groupWidget2, NoSpellIndex)); } else - mLines.push_back(LineInfo(groupWidget, (MyGUI::Widget*)NULL, NoSpellIndex)); + mLines.push_back(LineInfo(groupWidget, (MyGUI::Widget*)nullptr, NoSpellIndex)); } diff --git a/apps/openmw/mwgui/spellwindow.cpp b/apps/openmw/mwgui/spellwindow.cpp index 38de9288b..2177e9e2a 100644 --- a/apps/openmw/mwgui/spellwindow.cpp +++ b/apps/openmw/mwgui/spellwindow.cpp @@ -32,7 +32,7 @@ namespace MWGui SpellWindow::SpellWindow(DragAndDrop* drag) : WindowPinnableBase("openmw_spell_window.layout") , NoDrop(drag, mMainWidget) - , mSpellView(NULL) + , mSpellView(nullptr) , mUpdateTimer(0.0f) { mSpellIcons = new SpellIcons(); @@ -72,7 +72,7 @@ namespace MWGui // Reset the filter focus when opening the window MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getKeyFocusWidget(); if (focus == mFilterEdit) - MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(NULL); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(nullptr); updateSpells(); } diff --git a/apps/openmw/mwgui/statswindow.cpp b/apps/openmw/mwgui/statswindow.cpp index dfa029467..e58993a55 100644 --- a/apps/openmw/mwgui/statswindow.cpp +++ b/apps/openmw/mwgui/statswindow.cpp @@ -26,7 +26,7 @@ namespace MWGui StatsWindow::StatsWindow (DragAndDrop* drag) : WindowPinnableBase("openmw_stats_window.layout") , NoDrop(drag, mMainWidget) - , mSkillView(NULL) + , mSkillView(nullptr) , mMajorSkills() , mMinorSkills() , mMiscSkills() @@ -68,7 +68,7 @@ namespace MWGui for (int i = 0; i < ESM::Skill::Length; ++i) { mSkillValues.insert(std::make_pair(i, MWMechanics::SkillValue())); - mSkillWidgetMap.insert(std::make_pair(i, std::make_pair((MyGUI::TextBox*)NULL, (MyGUI::TextBox*)NULL))); + mSkillWidgetMap.insert(std::make_pair(i, std::make_pair((MyGUI::TextBox*)nullptr, (MyGUI::TextBox*)nullptr))); } MyGUI::Window* t = mMainWidget->castType(); diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index 726c72b99..4404b2b1a 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -50,8 +50,8 @@ namespace MWGui TradeWindow::TradeWindow() : WindowBase("openmw_trade_window.layout") - , mSortModel(NULL) - , mTradeModel(NULL) + , mSortModel(nullptr) + , mTradeModel(nullptr) , mItemToSell(-1) , mCurrentBalance(0) , mCurrentMerchantOffer(0) @@ -205,7 +205,7 @@ namespace MWGui else { mItemToSell = mSortModel->mapToSource(index); - sellItem (NULL, count); + sellItem (nullptr, count); } } @@ -514,8 +514,8 @@ namespace MWGui void TradeWindow::resetReference() { ReferenceInterface::resetReference(); - mItemView->setModel(NULL); - mTradeModel = NULL; - mSortModel = NULL; + mItemView->setModel(nullptr); + mTradeModel = nullptr; + mSortModel = nullptr; } } diff --git a/apps/openmw/mwgui/videowidget.cpp b/apps/openmw/mwgui/videowidget.cpp index 28432b811..5dec6e48a 100644 --- a/apps/openmw/mwgui/videowidget.cpp +++ b/apps/openmw/mwgui/videowidget.cpp @@ -16,7 +16,7 @@ namespace MWGui { VideoWidget::VideoWidget() - : mVFS(NULL) + : mVFS(nullptr) { mPlayer.reset(new Video::VideoPlayer()); setNeedKeyFocus(true); diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index db92b355c..037fcb19b 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -281,7 +281,7 @@ namespace MWGui mSleeping = canRest; Gui::Box* box = dynamic_cast(mMainWidget); - if (box == NULL) + if (box == nullptr) throw std::runtime_error("main widget must be a box"); box->notifyChildrenSizeChanged(); center(); diff --git a/apps/openmw/mwgui/widgets.cpp b/apps/openmw/mwgui/widgets.cpp index 024a91fc6..c3bc1ec19 100644 --- a/apps/openmw/mwgui/widgets.cpp +++ b/apps/openmw/mwgui/widgets.cpp @@ -23,8 +23,8 @@ namespace MWGui MWSkill::MWSkill() : mSkillId(ESM::Skill::Length) - , mSkillNameWidget(NULL) - , mSkillValueWidget(NULL) + , mSkillNameWidget(nullptr) + , mSkillValueWidget(nullptr) { } @@ -114,8 +114,8 @@ namespace MWGui MWAttribute::MWAttribute() : mId(-1) - , mAttributeNameWidget(NULL) - , mAttributeValueWidget(NULL) + , mAttributeNameWidget(nullptr) + , mAttributeValueWidget(nullptr) { } @@ -204,7 +204,7 @@ namespace MWGui /* MWSpell */ MWSpell::MWSpell() - : mSpellNameWidget(NULL) + : mSpellNameWidget(nullptr) { } @@ -286,7 +286,7 @@ namespace MWGui { // We don't know the width of all the elements beforehand, so we do it in // 2 steps: first, create all widgets and check their width.... - MWSpellEffectPtr effect = NULL; + MWSpellEffectPtr effect = nullptr; int maxwidth = coord.width; for (SpellEffectList::iterator it=mEffectList.begin(); @@ -359,8 +359,8 @@ namespace MWGui /* MWSpellEffect */ MWSpellEffect::MWSpellEffect() - : mImageWidget(NULL) - , mTextWidget(NULL) + : mImageWidget(nullptr) + , mTextWidget(nullptr) , mRequestedWidth(0) { } @@ -489,9 +489,9 @@ namespace MWGui MWDynamicStat::MWDynamicStat() : mValue(0) , mMax(1) - , mTextWidget(NULL) - , mBarWidget(NULL) - , mBarTextWidget(NULL) + , mTextWidget(nullptr) + , mBarWidget(nullptr) + , mBarTextWidget(nullptr) { } diff --git a/apps/openmw/mwgui/windowbase.cpp b/apps/openmw/mwgui/windowbase.cpp index 93440a50a..86c780325 100644 --- a/apps/openmw/mwgui/windowbase.cpp +++ b/apps/openmw/mwgui/windowbase.cpp @@ -33,11 +33,11 @@ void WindowBase::setVisible(bool visible) if (!visible) { MyGUI::Widget* keyFocus = MyGUI::InputManager::getInstance().getKeyFocusWidget(); - while (keyFocus != mMainWidget && keyFocus != NULL) + while (keyFocus != mMainWidget && keyFocus != nullptr) keyFocus = keyFocus->getParent(); if (keyFocus == mMainWidget) - MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(NULL); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(nullptr); } } diff --git a/apps/openmw/mwgui/windowbase.hpp b/apps/openmw/mwgui/windowbase.hpp index fde1f2ac9..7d8fb2f1e 100644 --- a/apps/openmw/mwgui/windowbase.hpp +++ b/apps/openmw/mwgui/windowbase.hpp @@ -23,7 +23,7 @@ namespace MWGui public: WindowBase(const std::string& parLayout); - virtual MyGUI::Widget* getDefaultKeyFocus() { return NULL; } + virtual MyGUI::Widget* getDefaultKeyFocus() { return nullptr; } // Events typedef MyGUI::delegates::CMultiDelegate1 EventHandle_WindowBase; diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 7fca0058f..e4515fdc3 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -134,44 +134,44 @@ namespace MWGui osgViewer::Viewer* viewer, osg::Group* guiRoot, Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue, const std::string& logpath, const std::string& resourcePath, bool consoleOnlyScripts, Translation::Storage& translationDataStorage, ToUTF8::FromType encoding, bool exportFonts, const std::map& fallbackMap, const std::string& versionDescription, const std::string& userDataPath) - : mStore(NULL) + : mStore(nullptr) , mResourceSystem(resourceSystem) , mWorkQueue(workQueue) , mViewer(viewer) , mConsoleOnlyScripts(consoleOnlyScripts) , mCurrentModals() - , mHud(NULL) - , mMap(NULL) - , mLocalMapRender(NULL) - , mToolTips(NULL) - , mStatsWindow(NULL) - , mMessageBoxManager(NULL) - , mConsole(NULL) - , mDialogueWindow(NULL) - , mDragAndDrop(NULL) - , mInventoryWindow(NULL) - , mScrollWindow(NULL) - , mBookWindow(NULL) - , mCountDialog(NULL) - , mTradeWindow(NULL) - , mSettingsWindow(NULL) - , mConfirmationDialog(NULL) - , mSpellWindow(NULL) - , mQuickKeysMenu(NULL) - , mLoadingScreen(NULL) - , mWaitDialog(NULL) - , mSoulgemDialog(NULL) - , mVideoBackground(NULL) - , mVideoWidget(NULL) - , mWerewolfFader(NULL) - , mBlindnessFader(NULL) - , mHitFader(NULL) - , mScreenFader(NULL) - , mDebugWindow(NULL) - , mJailScreen(NULL) + , mHud(nullptr) + , mMap(nullptr) + , mLocalMapRender(nullptr) + , mToolTips(nullptr) + , mStatsWindow(nullptr) + , mMessageBoxManager(nullptr) + , mConsole(nullptr) + , mDialogueWindow(nullptr) + , mDragAndDrop(nullptr) + , mInventoryWindow(nullptr) + , mScrollWindow(nullptr) + , mBookWindow(nullptr) + , mCountDialog(nullptr) + , mTradeWindow(nullptr) + , mSettingsWindow(nullptr) + , mConfirmationDialog(nullptr) + , mSpellWindow(nullptr) + , mQuickKeysMenu(nullptr) + , mLoadingScreen(nullptr) + , mWaitDialog(nullptr) + , mSoulgemDialog(nullptr) + , mVideoBackground(nullptr) + , mVideoWidget(nullptr) + , mWerewolfFader(nullptr) + , mBlindnessFader(nullptr) + , mHitFader(nullptr) + , mScreenFader(nullptr) + , mDebugWindow(nullptr) + , mJailScreen(nullptr) , mTranslationDataStorage (translationDataStorage) - , mCharGen(NULL) - , mInputBlocker(NULL) + , mCharGen(nullptr) + , mInputBlocker(nullptr) , mCrosshairEnabled(Settings::Manager::getBool ("crosshair", "HUD")) , mSubtitlesEnabled(Settings::Manager::getBool ("subtitles", "GUI")) , mHitFaderEnabled(Settings::Manager::getBool ("hit fader", "GUI")) @@ -185,9 +185,9 @@ namespace MWGui , mPlayerMajorSkills() , mPlayerMinorSkills() , mPlayerSkillValues() - , mGui(NULL) + , mGui(nullptr) , mGuiModes() - , mCursorManager(NULL) + , mCursorManager(nullptr) , mGarbageDialogs() , mShown(GW_ALL) , mForceHidden(GW_None) @@ -704,7 +704,7 @@ namespace MWGui setCursorVisible(!gameMode); if (gameMode) - setKeyFocusWidget (NULL); + setKeyFocusWidget (nullptr); // Icons of forced hidden windows are displayed setMinimapVisibility((mAllowed & GW_Map) && (!mMap->pinned() || (mForceHidden & GW_Map))); @@ -1651,7 +1651,7 @@ namespace MWGui // Remove this method for MyGUI 3.2.2 void WindowManager::setKeyFocusWidget(MyGUI::Widget *widget) { - if (widget == NULL) + if (widget == nullptr) MyGUI::InputManager::getInstance().resetKeyFocusWidget(); else MyGUI::InputManager::getInstance().setKeyFocusWidget(widget); @@ -1913,7 +1913,7 @@ namespace MWGui } if (mCurrentModals.empty()) { - mKeyboardNavigation->setModalWindow(NULL); + mKeyboardNavigation->setModalWindow(nullptr); mKeyboardNavigation->restoreFocus(getMode()); } else diff --git a/apps/openmw/mwgui/windowpinnablebase.cpp b/apps/openmw/mwgui/windowpinnablebase.cpp index a6984b5a4..271d4e3a8 100644 --- a/apps/openmw/mwgui/windowpinnablebase.cpp +++ b/apps/openmw/mwgui/windowpinnablebase.cpp @@ -14,7 +14,7 @@ namespace MWGui mPinButton->eventMouseButtonPressed += MyGUI::newDelegate(this, &WindowPinnableBase::onPinButtonPressed); - MyGUI::Button* button = NULL; + MyGUI::Button* button = nullptr; MyGUI::VectorWidgetPtr widgets = window->getSkinWidgetsByName("Action"); for (MyGUI::VectorWidgetPtr::iterator it = widgets.begin(); it != widgets.end(); ++it) { diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 101b4f2f7..b0a98bafc 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -46,9 +46,9 @@ namespace MWInput , mScreenCaptureHandler(screenCaptureHandler) , mScreenCaptureOperation(screenCaptureOperation) , mJoystickLastUsed(false) - , mPlayer(NULL) - , mInputManager(NULL) - , mVideoWrapper(NULL) + , mPlayer(nullptr) + , mInputManager(nullptr) + , mVideoWrapper(nullptr) , mUserFile(userFile) , mDragDrop(false) , mGrabCursor (Settings::Manager::getBool("grab cursor", "Input")) @@ -84,7 +84,7 @@ namespace MWInput Settings::Manager::getFloat("contrast", "Video")); std::string file = userFileExists ? userFile : ""; - mInputBinder = new ICS::InputControlSystem(file, true, this, NULL, A_Last); + mInputBinder = new ICS::InputControlSystem(file, true, this, nullptr, A_Last); loadKeyDefaults(); loadControllerDefaults(); diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index e18ba4119..2a68591a8 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -1420,7 +1420,7 @@ namespace MWMechanics iter->second->getCharacterController()->updateContinuousVfx(); // Animation/movement update - CharacterController* playerCharacter = NULL; + CharacterController* playerCharacter = nullptr; for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) { const float animationDistance = aiProcessingDistance + 400; // Slightly larger than AI distance so there is time to switch back to the idle animation. @@ -1987,7 +1987,7 @@ namespace MWMechanics for (; it != mActors.end(); ++it) { delete it->second; - it->second = NULL; + it->second = nullptr; } mActors.clear(); mDeathCount.clear(); diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 9824347e3..8f9545f99 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -586,7 +586,7 @@ std::string chooseBestAttack(const ESM::Weapon* weapon) { std::string attackType; - if (weapon != NULL) + if (weapon != nullptr) { //the more damage attackType deals the more probability it has int slash = (weapon->mData.mSlash[0] + weapon->mData.mSlash[1])/2; diff --git a/apps/openmw/mwmechanics/aicombat.hpp b/apps/openmw/mwmechanics/aicombat.hpp index 88feba481..f89e71678 100644 --- a/apps/openmw/mwmechanics/aicombat.hpp +++ b/apps/openmw/mwmechanics/aicombat.hpp @@ -64,7 +64,7 @@ namespace MWMechanics mAttackRange(0.0f), mCombatMove(false), mLastTargetPos(0,0,0), - mCell(NULL), + mCell(nullptr), mCurrentAction(), mActionCooldown(0.0f), mStrength(), diff --git a/apps/openmw/mwmechanics/aicombataction.cpp b/apps/openmw/mwmechanics/aicombataction.cpp index 6fffff637..804e0dd05 100644 --- a/apps/openmw/mwmechanics/aicombataction.cpp +++ b/apps/openmw/mwmechanics/aicombataction.cpp @@ -138,7 +138,7 @@ namespace MWMechanics const ESM::Weapon* ActionWeapon::getWeapon() const { if (mWeapon.isEmpty()) - return NULL; + return nullptr; return mWeapon.get()->mBase; } diff --git a/apps/openmw/mwmechanics/aicombataction.hpp b/apps/openmw/mwmechanics/aicombataction.hpp index 466ae2dc4..cf7dc78fd 100644 --- a/apps/openmw/mwmechanics/aicombataction.hpp +++ b/apps/openmw/mwmechanics/aicombataction.hpp @@ -17,7 +17,7 @@ namespace MWMechanics virtual void prepare(const MWWorld::Ptr& actor) = 0; virtual float getCombatRange (bool& isRanged) const = 0; virtual float getActionCooldown() { return 0.f; } - virtual const ESM::Weapon* getWeapon() const { return NULL; }; + virtual const ESM::Weapon* getWeapon() const { return nullptr; }; virtual bool isAttackingOrSpell() const { return true; } virtual bool isFleeing() const { return false; } }; diff --git a/apps/openmw/mwmechanics/aipackage.cpp b/apps/openmw/mwmechanics/aipackage.cpp index e6cca0523..9a42f191e 100644 --- a/apps/openmw/mwmechanics/aipackage.cpp +++ b/apps/openmw/mwmechanics/aipackage.cpp @@ -275,7 +275,7 @@ bool MWMechanics::AiPackage::shortcutPath(const ESM::Pathgrid::Point& startPoint static_cast(startPoint.mX), static_cast(startPoint.mY), static_cast(startPoint.mZ), static_cast(endPoint.mX), static_cast(endPoint.mY), static_cast(endPoint.mZ)); - if (destInLOS != NULL) *destInLOS = isPathClear; + if (destInLOS != nullptr) *destInLOS = isPathClear; if (!isPathClear) return false; diff --git a/apps/openmw/mwmechanics/aipackage.hpp b/apps/openmw/mwmechanics/aipackage.hpp index f1941ff1d..5a468ae30 100644 --- a/apps/openmw/mwmechanics/aipackage.hpp +++ b/apps/openmw/mwmechanics/aipackage.hpp @@ -114,7 +114,7 @@ namespace MWMechanics /// Check if there aren't any obstacles along the path to make shortcut possible /// If a shortcut is possible then path will be cleared and filled with the destination point. - /// \param destInLOS If not NULL function will return ray cast check result + /// \param destInLOS If not nullptr function will return ray cast check result /// \return If can shortcut the path bool shortcutPath(const ESM::Pathgrid::Point& startPoint, const ESM::Pathgrid::Point& endPoint, const MWWorld::Ptr& actor, bool *destInLOS, bool isPathClear); diff --git a/apps/openmw/mwmechanics/aistate.hpp b/apps/openmw/mwmechanics/aistate.hpp index 19f0ecf99..40f4ab1d4 100644 --- a/apps/openmw/mwmechanics/aistate.hpp +++ b/apps/openmw/mwmechanics/aistate.hpp @@ -59,7 +59,7 @@ namespace MWMechanics bool empty() const { - return mStorage == NULL; + return mStorage == nullptr; } const std::type_info& getType() const @@ -67,16 +67,12 @@ namespace MWMechanics return typeid(mStorage); } - - DerivedClassStorage():mStorage(NULL){} + DerivedClassStorage():mStorage(nullptr){} ~DerivedClassStorage() { if(mStorage) delete mStorage; } - - - }; diff --git a/apps/openmw/mwmechanics/aiwander.hpp b/apps/openmw/mwmechanics/aiwander.hpp index 4a0811f50..479ed4869 100644 --- a/apps/openmw/mwmechanics/aiwander.hpp +++ b/apps/openmw/mwmechanics/aiwander.hpp @@ -77,7 +77,7 @@ namespace MWMechanics mReaction(0), mSaidGreeting(Greet_None), mGreetingTimer(0), - mCell(NULL), + mCell(nullptr), mState(Wander_ChooseAction), mIsWanderingManually(false), mCanWanderAlongPathGrid(true), diff --git a/apps/openmw/mwmechanics/autocalcspell.cpp b/apps/openmw/mwmechanics/autocalcspell.cpp index 80ac14ddd..144449cf0 100644 --- a/apps/openmw/mwmechanics/autocalcspell.cpp +++ b/apps/openmw/mwmechanics/autocalcspell.cpp @@ -149,7 +149,7 @@ namespace MWMechanics float baseMagicka = fPCbaseMagickaMult * actorAttributes[ESM::Attribute::Intelligence]; bool reachedLimit = false; - const ESM::Spell* weakestSpell = NULL; + const ESM::Spell* weakestSpell = nullptr; int minCost = std::numeric_limits::max(); std::vector selectedSpells; diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index c4975b308..9a0ffa67d 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -931,7 +931,7 @@ CharacterController::~CharacterController() if (mAnimation) { persistAnimationState(); - mAnimation->setTextKeyListener(NULL); + mAnimation->setTextKeyListener(nullptr); } } @@ -1159,7 +1159,7 @@ bool CharacterController::updateCreatureState() if (!spellid.empty() && canCast) { - MWMechanics::CastSpell cast(mPtr, NULL, false, mCastingManualSpell); + MWMechanics::CastSpell cast(mPtr, nullptr, false, mCastingManualSpell); cast.playSpellCastingEffects(spellid); if (!mAnimation->hasAnimation("spellcast")) @@ -1463,7 +1463,7 @@ bool CharacterController::updateWeaponState(CharacterState& idle) if(!spellid.empty() && canCast) { - MWMechanics::CastSpell cast(mPtr, NULL, false, mCastingManualSpell); + MWMechanics::CastSpell cast(mPtr, nullptr, false, mCastingManualSpell); cast.playSpellCastingEffects(spellid); const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); @@ -2319,7 +2319,7 @@ void CharacterController::persistAnimationState() { anim.mLoopCount = mAnimation->getCurrentLoopCount(anim.mGroup); float complete; - mAnimation->getInfo(anim.mGroup, &complete, NULL); + mAnimation->getInfo(anim.mGroup, &complete, nullptr); anim.mTime = complete; } else @@ -2451,7 +2451,7 @@ bool CharacterController::isPersistentAnimPlaying() bool CharacterController::isAnimPlaying(const std::string &groupName) { - if(mAnimation == NULL) + if(mAnimation == nullptr) return false; return mAnimation->isPlaying(groupName); } @@ -2748,9 +2748,9 @@ void CharacterController::updateHeadTracking(float duration) if (const MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(mHeadTrackTarget)) { const osg::Node* node = anim->getNode("Head"); - if (node == NULL) + if (node == nullptr) node = anim->getNode("Bip01 Head"); - if (node != NULL) + if (node != nullptr) { nodepaths = node->getParentalNodePaths(); if (!nodepaths.empty()) diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 1db497828..f97614ef4 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -240,8 +240,8 @@ class CharacterController : public MWRender::Animation::TextKeyListener void playRandomDeath(float startpoint = 0.0f); /// choose a random animation group with \a prefix and numeric suffix - /// @param num if non-NULL, the chosen animation number will be written here - std::string chooseRandomGroup (const std::string& prefix, int* num = NULL) const; + /// @param num if non-nullptr, the chosen animation number will be written here + std::string chooseRandomGroup (const std::string& prefix, int* num = nullptr) const; bool updateCarriedLeftVisible(WeaponType weaptype) const; diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 33357b79a..6cef45e28 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -203,7 +203,7 @@ namespace MWMechanics } // F_PCStart spells - const ESM::Race* race = NULL; + const ESM::Race* race = nullptr; if (mRaceSelected) race = esmStore.get().find(player->mRace); diff --git a/apps/openmw/mwmechanics/objects.cpp b/apps/openmw/mwmechanics/objects.cpp index d8821276e..e95c0a704 100644 --- a/apps/openmw/mwmechanics/objects.cpp +++ b/apps/openmw/mwmechanics/objects.cpp @@ -20,7 +20,7 @@ Objects::~Objects() for (; it != mObjects.end();++it) { delete it->second; - it->second = NULL; + it->second = nullptr; } } diff --git a/apps/openmw/mwmechanics/obstacle.hpp b/apps/openmw/mwmechanics/obstacle.hpp index 6a84e0ef9..c55374501 100644 --- a/apps/openmw/mwmechanics/obstacle.hpp +++ b/apps/openmw/mwmechanics/obstacle.hpp @@ -16,7 +16,7 @@ namespace MWMechanics bool proximityToDoor(const MWWorld::Ptr& actor, float minDist); /// Returns door pointer within range. No guarantee is given as to which one - /** \return Pointer to the door, or NULL if none exists **/ + /** \return Pointer to the door, or empty pointer if none exists **/ const MWWorld::Ptr getNearbyDoor(const MWWorld::Ptr& actor, float minDist); class ObstacleCheck diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index b1b04f304..1da97a645 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -120,8 +120,8 @@ namespace MWMechanics } PathFinder::PathFinder() - : mPathgrid(NULL) - , mCell(NULL) + : mPathgrid(nullptr) + , mCell(nullptr) { } diff --git a/apps/openmw/mwmechanics/pathgrid.cpp b/apps/openmw/mwmechanics/pathgrid.cpp index ea4c973b7..6b5db64ea 100644 --- a/apps/openmw/mwmechanics/pathgrid.cpp +++ b/apps/openmw/mwmechanics/pathgrid.cpp @@ -50,8 +50,8 @@ namespace namespace MWMechanics { PathgridGraph::PathgridGraph(const MWWorld::CellStore *cell) - : mCell(NULL) - , mPathgrid(NULL) + : mCell(nullptr) + , mPathgrid(nullptr) , mIsExterior(0) , mGraph(0) , mIsGraphConstructed(false) diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index f1ee2520b..22631ca3f 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -242,9 +242,9 @@ namespace MWMechanics // This makes spells that are easy to cast harder to resist and vice versa float castChance = 100.f; - if (spell != NULL && !caster.isEmpty() && caster.getClass().isActor()) + if (spell != nullptr && !caster.isEmpty() && caster.getClass().isActor()) { - castChance = getSpellSuccessChance(spell, caster, NULL, false); // Uncapped casting chance + castChance = getSpellSuccessChance(spell, caster, nullptr, false); // Uncapped casting chance } if (castChance > 0) x *= 50 / castChance; @@ -737,7 +737,7 @@ namespace MWMechanics } else if (effectId == ESM::MagicEffect::Recall) { - MWWorld::CellStore* markedCell = NULL; + MWWorld::CellStore* markedCell = nullptr; ESM::Position markedPosition; MWBase::Environment::get().getWorld()->getPlayer().getMarkedPosition(markedCell, markedPosition); diff --git a/apps/openmw/mwmechanics/spellcasting.hpp b/apps/openmw/mwmechanics/spellcasting.hpp index 2844e7f23..5ac406368 100644 --- a/apps/openmw/mwmechanics/spellcasting.hpp +++ b/apps/openmw/mwmechanics/spellcasting.hpp @@ -39,8 +39,8 @@ namespace MWMechanics * @note actor can be an NPC or a creature * @return success chance from 0 to 100 (in percent), if cap=false then chance above 100 may be returned. */ - float getSpellSuccessChance (const ESM::Spell* spell, const MWWorld::Ptr& actor, int* effectiveSchool = NULL, bool cap=true, bool checkMagicka=false); - float getSpellSuccessChance (const std::string& spellId, const MWWorld::Ptr& actor, int* effectiveSchool = NULL, bool cap=true, bool checkMagicka=false); + float getSpellSuccessChance (const ESM::Spell* spell, const MWWorld::Ptr& actor, int* effectiveSchool = nullptr, bool cap=true, bool checkMagicka=false); + float getSpellSuccessChance (const std::string& spellId, const MWWorld::Ptr& actor, int* effectiveSchool = nullptr, bool cap=true, bool checkMagicka=false); int getSpellSchool(const std::string& spellId, const MWWorld::Ptr& actor); int getSpellSchool(const ESM::Spell* spell, const MWWorld::Ptr& actor); @@ -58,14 +58,14 @@ namespace MWMechanics /// @param effects Override the actor's current magicEffects. Useful if there are effects currently /// being applied (but not applied yet) that should also be considered. float getEffectResistance (short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, - const ESM::Spell* spell = NULL, const MagicEffects* effects = NULL); + const ESM::Spell* spell = nullptr, const MagicEffects* effects = nullptr); /// Get an effect multiplier for applying an effect cast by the given actor in the given spell (optional). /// @return effect multiplier from 0 to 2. (100% net resistance to 100% net weakness) /// @param effects Override the actor's current magicEffects. Useful if there are effects currently /// being applied (but not applied yet) that should also be considered. float getEffectMultiplier(short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster, - const ESM::Spell* spell = NULL, const MagicEffects* effects = NULL); + const ESM::Spell* spell = nullptr, const MagicEffects* effects = nullptr); bool checkEffectTarget (int effectId, const MWWorld::Ptr& target, const MWWorld::Ptr& caster, bool castByPlayer); diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 31da729ea..363f28e70 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -810,7 +810,7 @@ namespace MWPhysics btScalar mLeastDistSqr; DeepestNotMeContactTestResultCallback(const btCollisionObject* me, const std::vector& targets, const btVector3 &origin) - : mMe(me), mTargets(targets), mOrigin(origin), mObject(NULL), mContactPoint(0,0,0), + : mMe(me), mTargets(targets), mOrigin(origin), mObject(nullptr), mContactPoint(0,0,0), mLeastDistSqr(std::numeric_limits::max()) { } @@ -873,7 +873,7 @@ namespace MWPhysics object.setCollisionShape(&shape); object.setWorldTransform(btTransform(toBullet(orient), toBullet(center))); - const btCollisionObject* me = NULL; + const btCollisionObject* me = nullptr; std::vector targetCollisionObjects; const Actor* physactor = getActor(actor); @@ -906,7 +906,7 @@ namespace MWPhysics float PhysicsSystem::getHitDistance(const osg::Vec3f &point, const MWWorld::ConstPtr &target) const { - btCollisionObject* targetCollisionObj = NULL; + btCollisionObject* targetCollisionObj = nullptr; const Actor* actor = getActor(target); if (actor) targetCollisionObj = actor->getCollisionObject(); @@ -968,7 +968,7 @@ namespace MWPhysics btVector3 btFrom = toBullet(from); btVector3 btTo = toBullet(to); - const btCollisionObject* me = NULL; + const btCollisionObject* me = nullptr; std::vector targetCollisionObjects; if (!ignore.isEmpty()) @@ -1127,7 +1127,7 @@ namespace MWPhysics std::vector PhysicsSystem::getCollisions(const MWWorld::ConstPtr &ptr, int collisionGroup, int collisionMask) const { - btCollisionObject* me = NULL; + btCollisionObject* me = nullptr; ObjectMap::const_iterator found = mObjects.find(ptr); if (found != mObjects.end()) @@ -1255,7 +1255,7 @@ namespace MWPhysics ActorMap::iterator found = mActors.find(ptr); if (found != mActors.end()) return found->second; - return NULL; + return nullptr; } const Actor *PhysicsSystem::getActor(const MWWorld::ConstPtr &ptr) const @@ -1263,7 +1263,7 @@ namespace MWPhysics ActorMap::const_iterator found = mActors.find(ptr); if (found != mActors.end()) return found->second; - return NULL; + return nullptr; } const Object* PhysicsSystem::getObject(const MWWorld::ConstPtr &ptr) const @@ -1271,7 +1271,7 @@ namespace MWPhysics ObjectMap::const_iterator found = mObjects.find(ptr); if (found != mObjects.end()) return found->second; - return NULL; + return nullptr; } void PhysicsSystem::updateScale(const MWWorld::Ptr &ptr) diff --git a/apps/openmw/mwphysics/trace.cpp b/apps/openmw/mwphysics/trace.cpp index 14a5dac48..204ec1467 100644 --- a/apps/openmw/mwphysics/trace.cpp +++ b/apps/openmw/mwphysics/trace.cpp @@ -86,7 +86,7 @@ void ActorTracer::doTrace(const btCollisionObject *actor, const osg::Vec3f& star mPlaneNormal = osg::Vec3f(0.0f, 0.0f, 1.0f); mFraction = 1.0f; mHitPoint = end; - mHitObject = NULL; + mHitObject = nullptr; } } diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 2133f9835..6e2c76d1d 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -367,7 +367,7 @@ namespace void applyNode(osg::Node& node) { if (node.getStateSet()) - node.setStateSet(NULL); + node.setStateSet(nullptr); if (node.getNodeMask() == 0x1 && node.getNumParents() == 1) mToRemove.push_back(std::make_pair(&node, node.getParent(0))); @@ -635,12 +635,12 @@ namespace MWRender Animation::Animation(const MWWorld::Ptr &ptr, osg::ref_ptr parentNode, Resource::ResourceSystem* resourceSystem) : mInsert(parentNode) - , mSkeleton(NULL) + , mSkeleton(nullptr) , mNodeMapCreated(false) , mPtr(ptr) , mResourceSystem(resourceSystem) , mAccumulate(1.f, 1.f, 0.f) - , mTextKeyListener(NULL) + , mTextKeyListener(nullptr) , mHeadYawRadians(0.f) , mHeadPitchRadians(0.f) , mHasMagicEffects(false) @@ -826,7 +826,7 @@ namespace MWRender for(size_t i = 0;i < sNumBlendMasks;i++) mAnimationTimePtr[i]->setTimePtr(std::shared_ptr()); - mAccumCtrl = NULL; + mAccumCtrl = nullptr; mAnimSources.clear(); @@ -1084,12 +1084,12 @@ namespace MWRender node->removeUpdateCallback(it->second); // Should be no longer needed with OSG 3.4 - it->second->setNestedCallback(NULL); + it->second->setNestedCallback(nullptr); } mActiveControllers.clear(); - mAccumCtrl = NULL; + mAccumCtrl = nullptr; for(size_t blendMask = 0;blendMask < sNumBlendMasks;blendMask++) { @@ -1416,14 +1416,14 @@ namespace MWRender previousStateset = mObjectRoot->getStateSet(); mObjectRoot->getParent(0)->removeChild(mObjectRoot); } - mObjectRoot = NULL; - mSkeleton = NULL; + mObjectRoot = nullptr; + mSkeleton = nullptr; mNodeMap.clear(); mNodeMapCreated = false; mActiveControllers.clear(); - mAccumRoot = NULL; - mAccumCtrl = NULL; + mAccumRoot = nullptr; + mAccumCtrl = nullptr; if (!forceskeleton) { @@ -1556,7 +1556,7 @@ namespace MWRender node->addUpdateCallback(glowUpdater); // set a texture now so that the ShaderVisitor can find it - osg::ref_ptr writableStateSet = NULL; + osg::ref_ptr writableStateSet = nullptr; if (!node->getStateSet()) writableStateSet = node->getOrCreateStateSet(); else @@ -1735,7 +1735,7 @@ namespace MWRender std::string lowerName = Misc::StringUtils::lowerCase(name); NodeMap::const_iterator found = getNodeMap().find(lowerName); if (found == getNodeMap().end()) - return NULL; + return nullptr; else return found->second; } @@ -1766,7 +1766,7 @@ namespace MWRender } else { - mObjectRoot->setStateSet(NULL); + mObjectRoot->setStateSet(nullptr); mResourceSystem->getSceneManager()->recreateShaders(mObjectRoot); } @@ -1793,7 +1793,7 @@ namespace MWRender if (mGlowLight) { mInsert->removeChild(mGlowLight); - mGlowLight = NULL; + mGlowLight = nullptr; } } else @@ -1807,7 +1807,7 @@ namespace MWRender if (mGlowLight) { mInsert->removeChild(mGlowLight); - mGlowLight = NULL; + mGlowLight = nullptr; } osg::ref_ptr light (new osg::Light); @@ -1828,7 +1828,7 @@ namespace MWRender void Animation::addControllers() { - mHeadController = NULL; + mHeadController = nullptr; if (mPtr.getClass().isBipedal(mPtr)) { diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 2d8ac152f..47edbdc0b 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -425,7 +425,7 @@ public: * \param speedmult Stores the animation speed multiplier * \return True if the animation is active, false otherwise. */ - bool getInfo(const std::string &groupname, float *complete=NULL, float *speedmult=NULL) const; + bool getInfo(const std::string &groupname, float *complete=nullptr, float *speedmult=nullptr) const; /// Get the absolute position in the animation track of the first text key with the given group. float getStartTime(const std::string &groupname) const; @@ -453,7 +453,7 @@ public: /// This is typically called as part of runAnimation, but may be called manually if needed. void updateEffects(); - /// Return a node with the specified name, or NULL if not existing. + /// Return a node with the specified name, or nullptr if not existing. /// @note The matching is case-insensitive. const osg::Node* getNode(const std::string& name) const; diff --git a/apps/openmw/mwrender/bulletdebugdraw.cpp b/apps/openmw/mwrender/bulletdebugdraw.cpp index eb3775cb4..4cf76e473 100644 --- a/apps/openmw/mwrender/bulletdebugdraw.cpp +++ b/apps/openmw/mwrender/bulletdebugdraw.cpp @@ -54,9 +54,9 @@ void DebugDrawer::destroyGeometry() if (mGeometry) { mParentNode->removeChild(mGeometry); - mGeometry = NULL; - mVertices = NULL; - mDrawArrays = NULL; + mGeometry = nullptr; + mVertices = nullptr; + mDrawArrays = nullptr; } } diff --git a/apps/openmw/mwrender/camera.cpp b/apps/openmw/mwrender/camera.cpp index cb6188a54..74f2ba911 100644 --- a/apps/openmw/mwrender/camera.cpp +++ b/apps/openmw/mwrender/camera.cpp @@ -45,7 +45,7 @@ namespace MWRender Camera::Camera (osg::Camera* camera) : mHeightScale(1.f), mCamera(camera), - mAnimation(NULL), + mAnimation(nullptr), mFirstPersonView(true), mPreviewMode(false), mFreeLook(true), diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index 2e0249e60..075d2e03c 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -239,7 +239,7 @@ namespace MWRender void CharacterPreview::rebuild() { - mAnimation = NULL; + mAnimation = nullptr; mAnimation = new NpcAnimation(mCharacter, mNode, mResourceSystem, true, (renderHeadOnly() ? NpcAnimation::VM_HeadOnly : NpcAnimation::VM_Normal)); @@ -380,7 +380,7 @@ namespace MWRender void InventoryPreview::updatePtr(const MWWorld::Ptr &ptr) { - mCharacter = MWWorld::Ptr(ptr.getBase(), NULL); + mCharacter = MWWorld::Ptr(ptr.getBase(), nullptr); } void InventoryPreview::onSetup() @@ -403,7 +403,7 @@ namespace MWRender , mRef(&mBase) , mPitchRadians(osg::DegreesToRadians(6.f)) { - mCharacter = MWWorld::Ptr(&mRef, NULL); + mCharacter = MWWorld::Ptr(&mRef, nullptr); } RaceSelectionPreview::~RaceSelectionPreview() diff --git a/apps/openmw/mwrender/creatureanimation.cpp b/apps/openmw/mwrender/creatureanimation.cpp index 6db223bd5..a262d0021 100644 --- a/apps/openmw/mwrender/creatureanimation.cpp +++ b/apps/openmw/mwrender/creatureanimation.cpp @@ -170,7 +170,7 @@ void CreatureWeaponAnimation::releaseArrow(float attackStrength) osg::Group *CreatureWeaponAnimation::getArrowBone() { if (!mWeapon) - return NULL; + return nullptr; SceneUtil::FindByNameVisitor findVisitor ("ArrowBone"); mWeapon->getNode()->accept(findVisitor); @@ -180,7 +180,7 @@ osg::Group *CreatureWeaponAnimation::getArrowBone() osg::Node *CreatureWeaponAnimation::getWeaponNode() { - return mWeapon ? mWeapon->getNode().get() : NULL; + return mWeapon ? mWeapon->getNode().get() : nullptr; } Resource::ResourceSystem *CreatureWeaponAnimation::getResourceSystem() diff --git a/apps/openmw/mwrender/globalmap.cpp b/apps/openmw/mwrender/globalmap.cpp index 7712469e1..d1b6cd239 100644 --- a/apps/openmw/mwrender/globalmap.cpp +++ b/apps/openmw/mwrender/globalmap.cpp @@ -563,7 +563,7 @@ namespace MWRender requestOverlayTextureUpdate(0, 0, mWidth, mHeight, osg::ref_ptr(), true, false); - mWorkItem = NULL; + mWorkItem = nullptr; } } diff --git a/apps/openmw/mwrender/landmanager.cpp b/apps/openmw/mwrender/landmanager.cpp index 5edce2c3f..38c28a72a 100644 --- a/apps/openmw/mwrender/landmanager.cpp +++ b/apps/openmw/mwrender/landmanager.cpp @@ -14,7 +14,7 @@ namespace MWRender { LandManager::LandManager(int loadFlags) - : ResourceManager(NULL) + : ResourceManager(nullptr) , mLoadFlags(loadFlags) { } @@ -32,7 +32,7 @@ osg::ref_ptr LandManager::getLand(int x, int y) { const ESM::Land* land = MWBase::Environment::get().getWorld()->getStore().get().search(x,y); if (!land) - return NULL; + return nullptr; osg::ref_ptr landObj (new ESMTerrain::LandObject(land, mLoadFlags)); mCache->addEntryToObjectCache(idstr, landObj.get()); return landObj; diff --git a/apps/openmw/mwrender/landmanager.hpp b/apps/openmw/mwrender/landmanager.hpp index 392a8b406..81253bba3 100644 --- a/apps/openmw/mwrender/landmanager.hpp +++ b/apps/openmw/mwrender/landmanager.hpp @@ -19,7 +19,7 @@ namespace MWRender public: LandManager(int loadFlags); - /// @note Will return NULL if not found. + /// @note Will return nullptr if not found. osg::ref_ptr getLand(int x, int y); virtual void reportStats(unsigned int frameNumber, osg::Stats* stats) const; diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 2716ea19b..c7669f5e9 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -72,7 +72,7 @@ std::string getVampireHead(const std::string& race, bool female) } if (sVampireMapping.find(thisCombination) == sVampireMapping.end()) - sVampireMapping[thisCombination] = NULL; + sVampireMapping[thisCombination] = nullptr; const ESM::BodyPart* bodyPart = sVampireMapping[thisCombination]; if (!bodyPart) @@ -535,7 +535,7 @@ void NpcAnimation::updateParts() }; static const size_t slotlistsize = sizeof(slotlist)/sizeof(slotlist[0]); - bool wasArrowAttached = (mAmmunition.get() != NULL); + bool wasArrowAttached = (mAmmunition.get() != nullptr); mAmmunition.reset(); const MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr); @@ -823,7 +823,7 @@ void NpcAnimation::addPartGroup(int group, int priority, const std::vectormData.mPart == ESM::BodyPart::MP_Wrist || bodypart->mData.mPart == ESM::BodyPart::MP_Forearm || bodypart->mData.mPart == ESM::BodyPart::MP_Upperarm)) - bodypart = NULL; + bodypart = nullptr; } else if (!bodypart) Log(Debug::Warning) << "Warning: Failed to find body part '" << part->mFemale << "'"; @@ -838,7 +838,7 @@ void NpcAnimation::addPartGroup(int group, int priority, const std::vectormData.mPart == ESM::BodyPart::MP_Wrist || bodypart->mData.mPart == ESM::BodyPart::MP_Forearm || bodypart->mData.mPart == ESM::BodyPart::MP_Upperarm)) - bodypart = NULL; + bodypart = nullptr; } else if (!bodypart) Log(Debug::Warning) << "Warning: Failed to find body part '" << part->mMale << "'"; @@ -855,7 +855,7 @@ void NpcAnimation::addControllers() { Animation::addControllers(); - mFirstPersonNeckController = NULL; + mFirstPersonNeckController = nullptr; WeaponAnimation::deleteControllers(); if (mViewMode == VM_FirstPerson) @@ -947,7 +947,7 @@ osg::Group* NpcAnimation::getArrowBone() { PartHolderPtr part = mObjectParts[ESM::PRT_Weapon]; if (!part) - return NULL; + return nullptr; SceneUtil::FindByNameVisitor findVisitor ("ArrowBone"); part->getNode()->accept(findVisitor); @@ -959,7 +959,7 @@ osg::Node* NpcAnimation::getWeaponNode() { PartHolderPtr part = mObjectParts[ESM::PRT_Weapon]; if (!part) - return NULL; + return nullptr; return part->getNode(); } @@ -1083,7 +1083,7 @@ const std::vector& NpcAnimation::getBodyParts(const std:: sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Tail, ESM::PRT_Tail)); } - parts.resize(ESM::PRT_Count, NULL); + parts.resize(ESM::PRT_Count, nullptr); const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const MWWorld::Store &partStore = store.get(); diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index 7f3a8da2d..335ca5d5a 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -75,16 +75,16 @@ private: void updateNpcBase(); PartHolderPtr insertBoundedPart(const std::string &model, const std::string &bonename, - const std::string &bonefilter, bool enchantedGlow, osg::Vec4f* glowColor=NULL); + const std::string &bonefilter, bool enchantedGlow, osg::Vec4f* glowColor=nullptr); void removeIndividualPart(ESM::PartReferenceType type); void reserveIndividualPart(ESM::PartReferenceType type, int group, int priority); bool addOrReplaceIndividualPart(ESM::PartReferenceType type, int group, int priority, const std::string &mesh, - bool enchantedGlow=false, osg::Vec4f* glowColor=NULL); + bool enchantedGlow=false, osg::Vec4f* glowColor=nullptr); void removePartGroup(int group); void addPartGroup(int group, int priority, const std::vector &parts, - bool enchantedGlow=false, osg::Vec4f* glowColor=NULL); + bool enchantedGlow=false, osg::Vec4f* glowColor=nullptr); virtual void setRenderBin(); @@ -155,7 +155,7 @@ public: virtual void updatePtr(const MWWorld::Ptr& updated); /// Get a list of body parts that may be used by an NPC of given race and gender. - /// @note This is a fixed size list, one list item for each ESM::PartReferenceType, may contain NULL body parts. + /// @note This is a fixed size list, one list item for each ESM::PartReferenceType, may contain nullptr body parts. static const std::vector& getBodyParts(const std::string& raceId, bool female, bool firstperson, bool werewolf); }; diff --git a/apps/openmw/mwrender/objects.cpp b/apps/openmw/mwrender/objects.cpp index db6772eaa..a0f48ad27 100644 --- a/apps/openmw/mwrender/objects.cpp +++ b/apps/openmw/mwrender/objects.cpp @@ -126,14 +126,14 @@ bool Objects::removeObject (const MWWorld::Ptr& ptr) if (ptr.getClass().isActor()) { if (ptr.getClass().hasInventoryStore(ptr)) - ptr.getClass().getInventoryStore(ptr).setInvListener(NULL, ptr); + ptr.getClass().getInventoryStore(ptr).setInvListener(nullptr, ptr); - ptr.getClass().getContainerStore(ptr).setContListener(NULL); + ptr.getClass().getContainerStore(ptr).setContListener(nullptr); } ptr.getRefData().getBaseNode()->getParent(0)->removeChild(ptr.getRefData().getBaseNode()); - ptr.getRefData().setBaseNode(NULL); + ptr.getRefData().setBaseNode(nullptr); return true; } return false; @@ -153,8 +153,8 @@ void Objects::removeCell(const MWWorld::CellStore* store) if (ptr.getClass().isNpc() && ptr.getRefData().getCustomData()) { MWWorld::InventoryStore& invStore = ptr.getClass().getInventoryStore(ptr); - invStore.setInvListener(NULL, ptr); - invStore.setContListener(NULL); + invStore.setInvListener(nullptr, ptr); + invStore.setContListener(nullptr); } mObjects.erase(iter++); @@ -218,7 +218,7 @@ Animation* Objects::getAnimation(const MWWorld::Ptr &ptr) if(iter != mObjects.end()) return iter->second; - return NULL; + return nullptr; } const Animation* Objects::getAnimation(const MWWorld::ConstPtr &ptr) const @@ -227,7 +227,7 @@ const Animation* Objects::getAnimation(const MWWorld::ConstPtr &ptr) const if(iter != mObjects.end()) return iter->second; - return NULL; + return nullptr; } } diff --git a/apps/openmw/mwrender/pathgrid.cpp b/apps/openmw/mwrender/pathgrid.cpp index aae97fe35..ee4120d6f 100644 --- a/apps/openmw/mwrender/pathgrid.cpp +++ b/apps/openmw/mwrender/pathgrid.cpp @@ -27,8 +27,8 @@ namespace MWRender Pathgrid::Pathgrid(osg::ref_ptr root) : mPathgridEnabled(false) , mRootNode(root) - , mPathGridRoot(NULL) - , mInteriorPathgridNode(NULL) + , mPathGridRoot(nullptr) + , mInteriorPathgridNode(nullptr) { } @@ -94,7 +94,7 @@ void Pathgrid::togglePathgrid() if (mPathGridRoot) { mRootNode->removeChild(mPathGridRoot); - mPathGridRoot = NULL; + mPathGridRoot = nullptr; } } } @@ -124,7 +124,7 @@ void Pathgrid::enableCellPathgrid(const MWWorld::CellStore *store) } else { - assert(mInteriorPathgridNode == NULL); + assert(mInteriorPathgridNode == nullptr); mInteriorPathgridNode = cellPathGrid; } } @@ -146,7 +146,7 @@ void Pathgrid::disableCellPathgrid(const MWWorld::CellStore *store) if (mInteriorPathgridNode) { mPathGridRoot->removeChild(mInteriorPathgridNode); - mInteriorPathgridNode = NULL; + mInteriorPathgridNode = nullptr; } } } diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index ce0a41c9a..bb0bb2f13 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -232,7 +232,7 @@ namespace MWRender mObjects.reset(new Objects(mResourceSystem, sceneRoot, mUnrefQueue.get())); - if (getenv("OPENMW_DONT_PRECOMPILE") == NULL) + if (getenv("OPENMW_DONT_PRECOMPILE") == nullptr) { mViewer->setIncrementalCompileOperation(new osgUtil::IncrementalCompileOperation); mViewer->getIncrementalCompileOperation()->setTargetFrameRate(Settings::Manager::getFloat("target framerate", "Cells")); @@ -336,7 +336,7 @@ namespace MWRender RenderingManager::~RenderingManager() { // let background loading thread finish before we delete anything else - mWorkQueue = NULL; + mWorkQueue = nullptr; } MWRender::Objects& RenderingManager::getObjects() @@ -832,7 +832,7 @@ namespace MWRender stateset->setTextureAttributeAndModes(0,cubeTexture,osg::StateAttribute::ON); quad->setStateSet(stateset); - quad->setUpdateCallback(NULL); + quad->setUpdateCallback(nullptr); screenshotCamera->addChild(quad); @@ -975,7 +975,7 @@ namespace MWRender result.mHitNormalWorld = intersection.getWorldIntersectNormal(); result.mRatio = intersection.ratio; - PtrHolder* ptrHolder = NULL; + PtrHolder* ptrHolder = nullptr; for (osg::NodePath::const_iterator it = intersection.nodePath.begin(); it != intersection.nodePath.end(); ++it) { osg::UserDataContainer* userDataContainer = (*it)->getUserDataContainer(); @@ -1113,7 +1113,7 @@ namespace MWRender void RenderingManager::rebuildPtr(const MWWorld::Ptr &ptr) { - NpcAnimation *anim = NULL; + NpcAnimation *anim = nullptr; if(ptr == mPlayerAnimation->getPtr()) anim = mPlayerAnimation.get(); else diff --git a/apps/openmw/mwrender/ripplesimulation.cpp b/apps/openmw/mwrender/ripplesimulation.cpp index 141a3eb87..21bd48d9a 100644 --- a/apps/openmw/mwrender/ripplesimulation.cpp +++ b/apps/openmw/mwrender/ripplesimulation.cpp @@ -196,7 +196,7 @@ void RippleSimulation::emitRipple(const osg::Vec3f &pos) { if (std::abs(pos.z() - mParticleNode->getPosition().z()) < 20) { - osgParticle::Particle* p = mParticleSystem->createParticle(NULL); + osgParticle::Particle* p = mParticleSystem->createParticle(nullptr); p->setPosition(osg::Vec3f(pos.x(), pos.y(), 0.f)); p->setAngle(osg::Vec3f(0,0, Misc::Rng::rollProbability() * osg::PI * 2 - osg::PI)); } diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 24769019b..d6a0b9f01 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -629,7 +629,7 @@ private: if (mSunFlashNode) { mSunFlashNode->removeCullCallback(mSunFlashCallback); - mSunFlashCallback = NULL; + mSunFlashCallback = nullptr; } } @@ -671,7 +671,7 @@ private: if (mSunGlareNode) { mSunGlareNode->removeCullCallback(mSunGlareCallback); - mSunGlareCallback = NULL; + mSunGlareCallback = nullptr; } } @@ -1095,8 +1095,8 @@ private: SkyManager::SkyManager(osg::Group* parentNode, Resource::SceneManager* sceneManager) : mSceneManager(sceneManager) - , mCamera(NULL) - , mRainIntensityUniform(NULL) + , mCamera(nullptr) + , mRainIntensityUniform(nullptr) , mAtmosphereNightRoll(0.f) , mCreated(false) , mIsStorm(false) @@ -1302,7 +1302,7 @@ public: { if (stateset->getAttribute(osg::StateAttribute::MATERIAL)) { - SceneUtil::CompositeStateSetUpdater* composite = NULL; + SceneUtil::CompositeStateSetUpdater* composite = nullptr; osg::Callback* callback = node.getUpdateCallback(); while (callback) @@ -1383,12 +1383,12 @@ public: virtual osg::Object *cloneType() const override { - return NULL; + return nullptr; } virtual osg::Object *clone(const osg::CopyOp &op) const override { - return NULL; + return nullptr; } virtual void operate(osgParticle::Particle *P, double dt) override @@ -1520,10 +1520,10 @@ void SkyManager::destroyRain() return; mRootNode->removeChild(mRainNode); - mRainNode = NULL; - mRainParticleSystem = NULL; - mRainShooter = NULL; - mRainFader = NULL; + mRainNode = nullptr; + mRainParticleSystem = nullptr; + mRainShooter = nullptr; + mRainFader = nullptr; } SkyManager::~SkyManager() @@ -1531,7 +1531,7 @@ SkyManager::~SkyManager() if (mRootNode) { mRootNode->getParent(0)->removeChild(mRootNode); - mRootNode = NULL; + mRootNode = nullptr; } } @@ -1554,7 +1554,7 @@ bool SkyManager::isEnabled() bool SkyManager::hasRain() { - return mRainNode != NULL; + return mRainNode != nullptr; } void SkyManager::update(float duration) @@ -1658,7 +1658,7 @@ void SkyManager::setWeather(const WeatherResult& weather) if (mParticleEffect) { mParticleNode->removeChild(mParticleEffect); - mParticleEffect = NULL; + mParticleEffect = nullptr; mParticleFaders.clear(); } @@ -1667,7 +1667,7 @@ void SkyManager::setWeather(const WeatherResult& weather) if (mParticleNode) { mRootNode->removeChild(mParticleNode); - mParticleNode = NULL; + mParticleNode = nullptr; } } else diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index 8fd47d2c5..f668b0820 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -441,13 +441,13 @@ void Water::updateWaterMaterial() { mReflection->removeChildren(0, mReflection->getNumChildren()); mParent->removeChild(mReflection); - mReflection = NULL; + mReflection = nullptr; } if (mRefraction) { mRefraction->removeChildren(0, mRefraction->getNumChildren()); mParent->removeChild(mRefraction); - mRefraction = NULL; + mRefraction = nullptr; } if (Settings::Manager::getBool("shader", "Water")) @@ -579,7 +579,7 @@ void Water::createShaderWaterStateSet(osg::Node* node, Reflection* reflection, R shaderStateset->setAttributeAndModes(program, osg::StateAttribute::ON); node->setStateSet(shaderStateset); - node->setUpdateCallback(NULL); + node->setUpdateCallback(nullptr); } void Water::processChangedSettings(const Settings::CategorySettingVector& settings) @@ -595,13 +595,13 @@ Water::~Water() { mReflection->removeChildren(0, mReflection->getNumChildren()); mParent->removeChild(mReflection); - mReflection = NULL; + mReflection = nullptr; } if (mRefraction) { mRefraction->removeChildren(0, mRefraction->getNumChildren()); mParent->removeChild(mRefraction); - mRefraction = NULL; + mRefraction = nullptr; } } diff --git a/apps/openmw/mwrender/weaponanimation.cpp b/apps/openmw/mwrender/weaponanimation.cpp index 32990482a..d5a8cb10d 100644 --- a/apps/openmw/mwrender/weaponanimation.cpp +++ b/apps/openmw/mwrender/weaponanimation.cpp @@ -164,7 +164,7 @@ void WeaponAnimation::addControllers(const std::map >::const_iterator found = nodes.find(i == 0 ? "bip01 spine1" : "bip01 spine2"); if (found != nodes.end()) @@ -180,7 +180,7 @@ void WeaponAnimation::addControllers(const std::mapdisable(ptr); // resets runtime state such as inventory, stats and AI. does not reset position in the world - ptr.getRefData().setCustomData(NULL); + ptr.getRefData().setCustomData(nullptr); if (wasEnabled) MWBase::Environment::get().getWorld()->enable(ptr); } diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index 9d7a5c6ed..2695aed76 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -484,7 +484,7 @@ namespace MWScript if (!player.isInCell()) throw std::runtime_error("player not in a cell"); - MWWorld::CellStore* store = NULL; + MWWorld::CellStore* store = nullptr; if (player.getCell()->isExterior()) { int cx,cy; diff --git a/apps/openmw/mwsound/ffmpeg_decoder.cpp b/apps/openmw/mwsound/ffmpeg_decoder.cpp index 7dffd685a..53452c305 100644 --- a/apps/openmw/mwsound/ffmpeg_decoder.cpp +++ b/apps/openmw/mwsound/ffmpeg_decoder.cpp @@ -122,7 +122,7 @@ bool FFmpeg_Decoder::getAVAudioData() if(!mDataBuf || mDataBufLen < mFrame->nb_samples) { av_freep(&mDataBuf); - if(av_samples_alloc(&mDataBuf, NULL, av_get_channel_layout_nb_channels(mOutputChannelLayout), + if(av_samples_alloc(&mDataBuf, nullptr, av_get_channel_layout_nb_channels(mOutputChannelLayout), mFrame->nb_samples, mOutputSampleFormat, 0) < 0) return false; else @@ -181,34 +181,34 @@ void FFmpeg_Decoder::open(const std::string &fname) close(); mDataStream = mResourceMgr->get(fname); - if((mFormatCtx=avformat_alloc_context()) == NULL) + if((mFormatCtx=avformat_alloc_context()) == nullptr) throw std::runtime_error("Failed to allocate context"); try { - mFormatCtx->pb = avio_alloc_context(NULL, 0, 0, this, readPacket, writePacket, seek); - if(!mFormatCtx->pb || avformat_open_input(&mFormatCtx, fname.c_str(), NULL, NULL) != 0) + mFormatCtx->pb = avio_alloc_context(nullptr, 0, 0, this, readPacket, writePacket, seek); + if(!mFormatCtx->pb || avformat_open_input(&mFormatCtx, fname.c_str(), nullptr, nullptr) != 0) { // "Note that a user-supplied AVFormatContext will be freed on failure". if (mFormatCtx) { - if (mFormatCtx->pb != NULL) + if (mFormatCtx->pb != nullptr) { - if (mFormatCtx->pb->buffer != NULL) + if (mFormatCtx->pb->buffer != nullptr) { av_free(mFormatCtx->pb->buffer); - mFormatCtx->pb->buffer = NULL; + mFormatCtx->pb->buffer = nullptr; } av_free(mFormatCtx->pb); - mFormatCtx->pb = NULL; + mFormatCtx->pb = nullptr; } avformat_free_context(mFormatCtx); } - mFormatCtx = NULL; + mFormatCtx = nullptr; throw std::runtime_error("Failed to allocate input stream"); } - if(avformat_find_stream_info(mFormatCtx, NULL) < 0) + if(avformat_find_stream_info(mFormatCtx, nullptr) < 0) throw std::runtime_error("Failed to find stream info in "+fname); for(size_t j = 0;j < mFormatCtx->nb_streams;j++) @@ -231,7 +231,7 @@ void FFmpeg_Decoder::open(const std::string &fname) std::to_string((*mStream)->codec->codec_id); throw std::runtime_error(ss); } - if(avcodec_open2((*mStream)->codec, codec, NULL) < 0) + if(avcodec_open2((*mStream)->codec, codec, nullptr) < 0) throw std::runtime_error(std::string("Failed to open audio codec ") + codec->long_name); @@ -255,17 +255,17 @@ void FFmpeg_Decoder::open(const std::string &fname) { if(mStream) avcodec_close((*mStream)->codec); - mStream = NULL; + mStream = nullptr; - if (mFormatCtx != NULL) + if (mFormatCtx != nullptr) { - if (mFormatCtx->pb->buffer != NULL) + if (mFormatCtx->pb->buffer != nullptr) { av_free(mFormatCtx->pb->buffer); - mFormatCtx->pb->buffer = NULL; + mFormatCtx->pb->buffer = nullptr; } av_free(mFormatCtx->pb); - mFormatCtx->pb = NULL; + mFormatCtx->pb = nullptr; avformat_close_input(&mFormatCtx); } @@ -276,7 +276,7 @@ void FFmpeg_Decoder::close() { if(mStream) avcodec_close((*mStream)->codec); - mStream = NULL; + mStream = nullptr; av_free_packet(&mPacket); av_freep(&mFrame); @@ -285,20 +285,20 @@ void FFmpeg_Decoder::close() if(mFormatCtx) { - if (mFormatCtx->pb != NULL) + if (mFormatCtx->pb != nullptr) { // mFormatCtx->pb->buffer must be freed by hand, // if not, valgrind will show memleak, see: // // https://trac.ffmpeg.org/ticket/1357 // - if (mFormatCtx->pb->buffer != NULL) + if (mFormatCtx->pb->buffer != nullptr) { av_free(mFormatCtx->pb->buffer); - mFormatCtx->pb->buffer = NULL; + mFormatCtx->pb->buffer = nullptr; } av_free(mFormatCtx->pb); - mFormatCtx->pb = NULL; + mFormatCtx->pb = nullptr; } avformat_close_input(&mFormatCtx); } @@ -373,7 +373,7 @@ void FFmpeg_Decoder::getInfo(int *samplerate, ChannelConfig *chans, SampleType * (*mStream)->codec->sample_fmt, // input sample format (*mStream)->codec->sample_rate, // input sample rate 0, // logging level offset - NULL); // log context + nullptr); // log context if(!mSwr) throw std::runtime_error("Couldn't allocate SwrContext"); if(swr_init(mSwr) < 0) @@ -417,17 +417,17 @@ size_t FFmpeg_Decoder::getSampleOffset() FFmpeg_Decoder::FFmpeg_Decoder(const VFS::Manager* vfs) : Sound_Decoder(vfs) - , mFormatCtx(NULL) - , mStream(NULL) - , mFrame(NULL) + , mFormatCtx(nullptr) + , mStream(nullptr) + , mFrame(nullptr) , mFrameSize(0) , mFramePos(0) , mNextPts(0.0) , mSwr(0) , mOutputSampleFormat(AV_SAMPLE_FMT_NONE) , mOutputChannelLayout(0) - , mDataBuf(NULL) - , mFrameData(NULL) + , mDataBuf(nullptr) + , mFrameData(nullptr) , mDataBufLen(0) { memset(&mPacket, 0, sizeof(mPacket)); diff --git a/apps/openmw/mwsound/movieaudiofactory.cpp b/apps/openmw/mwsound/movieaudiofactory.cpp index 497562516..faa8d94ca 100644 --- a/apps/openmw/mwsound/movieaudiofactory.cpp +++ b/apps/openmw/mwsound/movieaudiofactory.cpp @@ -19,7 +19,7 @@ namespace MWSound { public: MWSoundDecoderBridge(MWSound::MovieAudioDecoder* decoder) - : Sound_Decoder(NULL) + : Sound_Decoder(nullptr) , mDecoder(decoder) { } diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index cadcdc4ab..39e872b0c 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -572,10 +572,10 @@ std::vector OpenAL_Output::enumerate() std::vector devlist; const ALCchar *devnames; - if(alcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT")) - devnames = alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER); + if(alcIsExtensionPresent(nullptr, "ALC_ENUMERATE_ALL_EXT")) + devnames = alcGetString(nullptr, ALC_ALL_DEVICES_SPECIFIER); else - devnames = alcGetString(NULL, ALC_DEVICE_SPECIFIER); + devnames = alcGetString(nullptr, ALC_DEVICE_SPECIFIER); while(devnames && *devnames) { devlist.push_back(devnames); diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 9c12584d7..8e3488906 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -819,7 +819,7 @@ namespace MWSound } const ESM::Region *regn = world->getStore().get().search(regionName); - if(regn == NULL) + if(regn == nullptr) return; if(total == 0) diff --git a/apps/openmw/mwstate/charactermanager.cpp b/apps/openmw/mwstate/charactermanager.cpp index a91bd4e8c..d440ba869 100644 --- a/apps/openmw/mwstate/charactermanager.cpp +++ b/apps/openmw/mwstate/charactermanager.cpp @@ -46,7 +46,7 @@ void MWState::CharacterManager::deleteSlot(const MWState::Character *character, // All slots deleted, cleanup and remove this character it->cleanup(); if (character == mCurrent) - mCurrent = NULL; + mCurrent = nullptr; mCharacters.erase(it); } } @@ -96,7 +96,7 @@ std::list::iterator MWState::CharacterManager::findCharacter void MWState::CharacterManager::setCurrentCharacter (const Character *character) { if (!character) - mCurrent = NULL; + mCurrent = nullptr; else { std::list::iterator it = findCharacter(character); diff --git a/apps/openmw/mwstate/quicksavemanager.cpp b/apps/openmw/mwstate/quicksavemanager.cpp index 59658ce6e..df078e026 100644 --- a/apps/openmw/mwstate/quicksavemanager.cpp +++ b/apps/openmw/mwstate/quicksavemanager.cpp @@ -20,7 +20,7 @@ void MWState::QuickSaveManager::visitSave(const Slot *saveSlot) bool MWState::QuickSaveManager::isOldestSave(const Slot *compare) { - if(mOldestSlotVisited == NULL) + if(mOldestSlotVisited == nullptr) return true; return (compare->mTimeStamp <= mOldestSlotVisited->mTimeStamp); } @@ -33,6 +33,6 @@ bool MWState::QuickSaveManager::shouldCreateNewSlot() const MWState::Slot *MWState::QuickSaveManager::getNextQuickSaveSlot() { if(shouldCreateNewSlot()) - return NULL; + return nullptr; return mOldestSlotVisited; } diff --git a/apps/openmw/mwstate/quicksavemanager.hpp b/apps/openmw/mwstate/quicksavemanager.hpp index e52cd609f..a5237d7c3 100644 --- a/apps/openmw/mwstate/quicksavemanager.hpp +++ b/apps/openmw/mwstate/quicksavemanager.hpp @@ -28,7 +28,7 @@ namespace MWState{ const Slot *getNextQuickSaveSlot(); ///< Get the slot that the next quicksave should use. /// - ///\return Either the oldest quicksave slot visited, or NULL if a new slot can be made + ///\return Either the oldest quicksave slot visited, or nullptr if a new slot can be made }; } diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 560433820..9804531d7 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -55,7 +55,7 @@ void MWState::StateManager::cleanup (bool force) MWBase::Environment::get().getMechanicsManager()->clear(); mState = State_NoGame; - mCharacterManager.setCurrentCharacter(NULL); + mCharacterManager.setCurrentCharacter(nullptr); mTimePlayed = 0; MWMechanics::CreatureStats::cleanup(); diff --git a/apps/openmw/mwworld/cellpreloader.cpp b/apps/openmw/mwworld/cellpreloader.cpp index 9163f1f2e..a06c208b5 100644 --- a/apps/openmw/mwworld/cellpreloader.cpp +++ b/apps/openmw/mwworld/cellpreloader.cpp @@ -206,13 +206,13 @@ namespace MWWorld { mTerrainPreloadItem->abort(); mTerrainPreloadItem->waitTillDone(); - mTerrainPreloadItem = NULL; + mTerrainPreloadItem = nullptr; } if (mUpdateCacheItem) { mUpdateCacheItem->waitTillDone(); - mUpdateCacheItem = NULL; + mUpdateCacheItem = nullptr; } for (PreloadMap::iterator it = mPreloadCells.begin(); it != mPreloadCells.end();++it) diff --git a/apps/openmw/mwworld/cells.cpp b/apps/openmw/mwworld/cells.cpp index 8506cefa3..2b032e1d7 100644 --- a/apps/openmw/mwworld/cells.cpp +++ b/apps/openmw/mwworld/cells.cpp @@ -340,7 +340,7 @@ public: } catch (...) { - return NULL; + return nullptr; } } }; diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 5cd8346a7..239d714a0 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -156,7 +156,7 @@ namespace ESM::RefNum mRefNumToFind; SearchByRefNumVisitor(const ESM::RefNum& toFind) - : mFound(NULL) + : mFound(nullptr) , mRefNumToFind(toFind) { } @@ -259,7 +259,7 @@ namespace MWWorld { MWWorld::Ptr copied = object.getClass().copyToCell(object, *cellToMoveTo, object.getRefData().getCount()); object.getRefData().setCount(0); - object.getRefData().setBaseNode(NULL); + object.getRefData().setBaseNode(nullptr); return copied; } @@ -901,7 +901,7 @@ namespace MWWorld CellStore* otherCell = callback->getCellStore(movedTo); - if (otherCell == NULL) + if (otherCell == nullptr) { 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."; diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index bcf4b4ada..1c4d8f5d8 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -64,7 +64,7 @@ namespace MWWorld // Even though fog actually belongs to the player and not cells, // it makes sense to store it here since we need it once for each cell. - // Note this is NULL until the cell is explored to save some memory + // Note this is nullptr until the cell is explored to save some memory std::shared_ptr mFogState; const ESM::Cell *mCell; @@ -367,7 +367,7 @@ namespace MWWorld struct GetCellStoreCallback { public: - ///@note must return NULL if the cell is not found + ///@note must return nullptr if the cell is not found virtual CellStore* getCellStore(const ESM::CellId& cellId) = 0; }; diff --git a/apps/openmw/mwworld/cellvisitors.hpp b/apps/openmw/mwworld/cellvisitors.hpp index cfb07f749..e68b383b7 100644 --- a/apps/openmw/mwworld/cellvisitors.hpp +++ b/apps/openmw/mwworld/cellvisitors.hpp @@ -17,7 +17,7 @@ namespace MWWorld { if (ptr.getRefData().getBaseNode()) { - ptr.getRefData().setBaseNode(NULL); + ptr.getRefData().setBaseNode(nullptr); mObjects.push_back (ptr); } diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 8fa16ff51..6a24ae4f0 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -114,7 +114,7 @@ void MWWorld::ContainerStore::storeStates (const CellRefList& collection, const std::string MWWorld::ContainerStore::sGoldId = "gold_001"; -MWWorld::ContainerStore::ContainerStore() : mListener(NULL), mCachedWeight (0), mWeightUpToDate (false) {} +MWWorld::ContainerStore::ContainerStore() : mListener(nullptr), mCachedWeight (0), mWeightUpToDate (false) {} MWWorld::ContainerStore::~ContainerStore() {} @@ -286,7 +286,7 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr MWWorld::Ptr item = *it; // we may have copied an item from the world, so reset a few things first - item.getRefData().setBaseNode(NULL); // Especially important, otherwise scripts on the item could think that it's actually in a cell + item.getRefData().setBaseNode(nullptr); // Especially important, otherwise scripts on the item could think that it's actually in a cell ESM::Position pos; pos.rot[0] = 0; pos.rot[1] = 0; diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 7f3018c55..2ca7b94ee 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -99,7 +99,7 @@ void MWWorld::InventoryStore::readEquipmentState(const MWWorld::ContainerStoreIt } MWWorld::InventoryStore::InventoryStore() - : mListener(NULL) + : mListener(nullptr) , mUpdatesEnabled (true) , mFirstAutoEquip(true) , mSelectedEnchantItem(end()) diff --git a/apps/openmw/mwworld/livecellref.hpp b/apps/openmw/mwworld/livecellref.hpp index 2631f513f..4b61c0a08 100644 --- a/apps/openmw/mwworld/livecellref.hpp +++ b/apps/openmw/mwworld/livecellref.hpp @@ -76,11 +76,11 @@ namespace MWWorld template struct LiveCellRef : public LiveCellRefBase { - LiveCellRef(const ESM::CellRef& cref, const X* b = NULL) + LiveCellRef(const ESM::CellRef& cref, const X* b = nullptr) : LiveCellRefBase(typeid(X).name(), cref), mBase(b) {} - LiveCellRef(const X* b = NULL) + LiveCellRef(const X* b = nullptr) : LiveCellRefBase(typeid(X).name()), mBase(b) {} diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index 49b7dd8bb..85181e998 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -30,7 +30,7 @@ namespace MWWorld Player::Player (const ESM::NPC *player) : mCellStore(0), mLastKnownExteriorPosition(0,0,0), - mMarkedCell(NULL), + mMarkedCell(nullptr), mAutoMove(false), mForwardBackward(0), mTeleported(false), @@ -406,7 +406,7 @@ namespace MWWorld { 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; + mCellStore = nullptr; } if (!player.mBirthsign.empty()) diff --git a/apps/openmw/mwworld/player.hpp b/apps/openmw/mwworld/player.hpp index 4b92fa396..7f37de010 100644 --- a/apps/openmw/mwworld/player.hpp +++ b/apps/openmw/mwworld/player.hpp @@ -38,7 +38,7 @@ namespace MWWorld osg::Vec3f mLastKnownExteriorPosition; ESM::Position mMarkedPosition; - // If no position was marked, this is NULL + // If no position was marked, this is nullptr CellStore* mMarkedCell; bool mAutoMove; diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index dc3d4fd30..30e73df58 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -94,7 +94,7 @@ namespace void updateObjectScale(const MWWorld::Ptr& ptr, MWPhysics::PhysicsSystem& physics, MWRender::RenderingManager& rendering) { - if (ptr.getRefData().getBaseNode() != NULL) + if (ptr.getRefData().getBaseNode() != nullptr) { float scale = ptr.getCellRef().getScale(); osg::Vec3f scaleVec (scale, scale, scale); @@ -330,7 +330,7 @@ namespace MWWorld while (active!=mActiveCells.end()) unloadCell (active++); assert(mActiveCells.empty()); - mCurrentCell = NULL; + mCurrentCell = nullptr; mPreloader->clear(); } @@ -519,7 +519,7 @@ namespace MWWorld void Scene::changeToInteriorCell (const std::string& cellName, const ESM::Position& position, bool adjustPlayerPos, bool changeEvent) { CellStore *cell = MWBase::Environment::get().getWorld()->getInterior(cellName); - bool loadcell = (mCurrentCell == NULL); + bool loadcell = (mCurrentCell == nullptr); if(!loadcell) loadcell = *mCurrentCell != *cell; diff --git a/apps/openmw/mwworld/store.cpp b/apps/openmw/mwworld/store.cpp index 2ac78bb85..b6bf2b7eb 100644 --- a/apps/openmw/mwworld/store.cpp +++ b/apps/openmw/mwworld/store.cpp @@ -96,7 +96,7 @@ namespace MWWorld typename Static::const_iterator it = mStatic.find(index); if (it != mStatic.end()) return &(it->second); - return NULL; + return nullptr; } template const T *IndexedStore::find(int index) const @@ -165,7 +165,7 @@ namespace MWWorld std::for_each(mShared.begin(), mShared.end(), GetRecords(id, &results)); if(!results.empty()) return results[Misc::Rng::rollDice(results.size())]; - return NULL; + return nullptr; } template const T *Store::find(const std::string &id) const @@ -358,7 +358,7 @@ namespace MWWorld const LandTextureList <exl = mStatic[plugin]; if (index >= ltexl.size()) - return NULL; + return nullptr; return <exl[index]; } const ESM::LandTexture *Store::find(size_t index, size_t plugin) const @@ -867,7 +867,7 @@ namespace MWWorld //========================================================================= Store::Store() - : mCells(NULL) + : mCells(nullptr) { } @@ -887,7 +887,7 @@ namespace MWWorld // mX and mY will be (0,0) for interior cells, but there is also an exterior cell with the coordinates of (0,0), so that doesn't help. // Check whether mCell is an interior cell. This isn't perfect, will break if a Region with the same name as an interior cell is created. // A proper fix should be made for future versions of the file format. - bool interior = mCells->search(pathgrid.mCell) != NULL; + bool interior = mCells->search(pathgrid.mCell) != nullptr; // Try to overwrite existing record if (interior) @@ -917,14 +917,14 @@ namespace MWWorld Exterior::const_iterator it = mExt.find(std::make_pair(x,y)); if (it != mExt.end()) return &(it->second); - return NULL; + return nullptr; } const ESM::Pathgrid *Store::search(const std::string& name) const { Interior::const_iterator it = mInt.find(name); if (it != mInt.end()) return &(it->second); - return NULL; + return nullptr; } const ESM::Pathgrid *Store::find(int x, int y) const { diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index 00ea6f66f..2ed81af48 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -166,7 +166,7 @@ namespace MWWorld */ bool isDynamic(const std::string &id) const; - /** Returns a random record that starts with the named ID, or NULL if not found. */ + /** Returns a random record that starts with the named ID, or nullptr if not found. */ const T *searchRandom(const std::string &id) const; const T *find(const std::string &id) const; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 3cb6b3a59..0dcdea131 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1111,7 +1111,7 @@ namespace MWWorld void World::deleteObject (const Ptr& ptr) { - if (!ptr.getRefData().isDeleted() && ptr.getContainerStore() == NULL) + if (!ptr.getRefData().isDeleted() && ptr.getContainerStore() == nullptr) { if (ptr == getPlayerPtr()) throw std::runtime_error("can not delete player object"); @@ -2192,7 +2192,7 @@ namespace MWWorld pos.z() += heightRatio*2*mPhysics->getRenderingHalfExtents(object).z(); - const CellStore *currCell = object.isInCell() ? object.getCell() : NULL; // currCell == NULL should only happen for player, during initial startup + const CellStore *currCell = object.isInCell() ? object.getCell() : nullptr; // currCell == nullptr should only happen for player, during initial startup return isUnderwater(currCell, pos); } @@ -2867,7 +2867,7 @@ namespace MWWorld // if the faced object can not be activated, do not use it if (!target.isEmpty() && !target.getClass().canBeActivated(target)) - target = NULL; + target = nullptr; if (target.isEmpty()) { @@ -2920,14 +2920,14 @@ namespace MWWorld target = result1.first; hitPosition = result1.second; if (dist1 > getMaxActivationDistance()) - target = NULL; + target = nullptr; } else if (result2.mHit) { target = result2.mHitObject; hitPosition = result2.mHitPointWorld; if (dist2 > getMaxActivationDistance() && !target.isEmpty() && !target.getClass().canBeActivated(target)) - target = NULL; + target = nullptr; } } } diff --git a/apps/openmw_test_suite/mwworld/test_store.cpp b/apps/openmw_test_suite/mwworld/test_store.cpp index ac21470de..63e4bd6af 100644 --- a/apps/openmw_test_suite/mwworld/test_store.cpp +++ b/apps/openmw_test_suite/mwworld/test_store.cpp @@ -28,7 +28,7 @@ struct ContentFileTest : public ::testing::Test for (std::vector::const_iterator it = mContentFiles.begin(); it != mContentFiles.end(); ++it) { ESM::ESMReader lEsm; - lEsm.setEncoder(NULL); + lEsm.setEncoder(nullptr); lEsm.setIndex(index); lEsm.setGlobalReaderList(&readerList); lEsm.open(it->string()); @@ -309,7 +309,7 @@ TEST_F(StoreTest, overwrite_test) // verify that changes were actually applied const RecordType* overwrittenRec = mEsmStore.get().search(recordId); - ASSERT_TRUE (overwrittenRec != NULL); + ASSERT_TRUE (overwrittenRec != nullptr); ASSERT_TRUE (overwrittenRec && overwrittenRec->mModel == "the_new_model"); } diff --git a/apps/wizard/existinginstallationpage.cpp b/apps/wizard/existinginstallationpage.cpp index a04e721d8..3ec98a935 100644 --- a/apps/wizard/existinginstallationpage.cpp +++ b/apps/wizard/existinginstallationpage.cpp @@ -96,7 +96,7 @@ void Wizard::ExistingInstallationPage::on_browseButton_clicked() tr("Select master file"), QDir::currentPath(), QString(tr("Morrowind master file (*.esm)")), - NULL, + nullptr, QFileDialog::DontResolveSymlinks); if (selectedFile.isEmpty()) diff --git a/components/contentselector/view/contentselector.cpp b/components/contentselector/view/contentselector.cpp index e26f9a942..4f95b7fe4 100644 --- a/components/contentselector/view/contentselector.cpp +++ b/components/contentselector/view/contentselector.cpp @@ -196,7 +196,7 @@ void ContentSelectorView::ContentSelector::setGameFileSelected(int index, bool s { QString fileName = ui.gameFileView->itemText(index); const ContentSelectorModel::EsmFile* file = mContentModel->item(fileName); - if (file != NULL) + if (file != nullptr) { QModelIndex index2(mContentModel->indexFromItem(file)); mContentModel->setData(index2, selected, Qt::UserRole + 1); diff --git a/components/crashcatcher/crashcatcher.cpp b/components/crashcatcher/crashcatcher.cpp index 64824b6b3..2e2ddd1f2 100644 --- a/components/crashcatcher/crashcatcher.cpp +++ b/components/crashcatcher/crashcatcher.cpp @@ -71,7 +71,7 @@ static const struct { { "Illegal instruction", SIGILL }, { "FPU exception", SIGFPE }, { "System BUS error", SIGBUS }, -{ NULL, 0 } +{ nullptr, 0 } }; static const struct { @@ -88,7 +88,7 @@ static const struct { { ILL_COPROC, "Coprocessor error" }, { ILL_BADSTK, "Internal stack error" }, #endif - { 0, NULL } + { 0, nullptr } }; static const struct { @@ -103,7 +103,7 @@ static const struct { { FPE_FLTRES, "Floating point inexact result" }, { FPE_FLTINV, "Floating point invalid operation" }, { FPE_FLTSUB, "Subscript out of range" }, - { 0, NULL } + { 0, nullptr } }; static const struct { @@ -114,7 +114,7 @@ static const struct { { SEGV_MAPERR, "Address not mapped to object" }, { SEGV_ACCERR, "Invalid permissions for mapped object" }, #endif - { 0, NULL } + { 0, nullptr } }; static const struct { @@ -126,7 +126,7 @@ static const struct { { BUS_ADRERR, "Non-existent physical address" }, { BUS_OBJERR, "Object specific hardware error" }, #endif - { 0, NULL } + { 0, nullptr } }; static int (*cc_user_info)(char*, char*); @@ -140,7 +140,7 @@ static void gdb_info(pid_t pid) /* Create a temp file to put gdb commands into */ strcpy(respfile, "/tmp/gdb-respfile-XXXXXX"); - if((fd=mkstemp(respfile)) >= 0 && (f=fdopen(fd, "w")) != NULL) + if((fd=mkstemp(respfile)) >= 0 && (f=fdopen(fd, "w")) != nullptr) { fprintf(f, "attach %d\n" "shell echo \"\"\n" @@ -267,7 +267,7 @@ static void crash_catcher(int signum, siginfo_t *siginfo, void *context) close(fd[0]); close(fd[1]); - execl(argv0, argv0, crash_switch, NULL); + execl(argv0, argv0, crash_switch, nullptr); safe_write(STDERR_FILENO, exec_err, sizeof(exec_err)-1); _exit(1); @@ -407,7 +407,7 @@ static void crash_handler(const char *logfile) if(logfile) { std::string message = "OpenMW has encountered a fatal error.\nCrash log saved to '" + std::string(logfile) + "'.\n Please report this to https://bugs.openmw.org !"; - SDL_ShowSimpleMessageBox(0, "Fatal Error", message.c_str(), NULL); + SDL_ShowSimpleMessageBox(0, "Fatal Error", message.c_str(), nullptr); } exit(0); @@ -444,7 +444,7 @@ int crashCatcherInstallHandlers(int argc, char **argv, int num_signals, int *sig altss.ss_sp = altstack; altss.ss_flags = 0; altss.ss_size = sizeof(altstack); - sigaltstack(&altss, NULL); + sigaltstack(&altss, nullptr); memset(&sa, 0, sizeof(sa)); sa.sa_sigaction = crash_catcher; @@ -455,7 +455,7 @@ int crashCatcherInstallHandlers(int argc, char **argv, int num_signals, int *sig while(num_signals--) { if((*signals != SIGSEGV && *signals != SIGILL && *signals != SIGFPE && *signals != SIGABRT && - *signals != SIGBUS) || sigaction(*signals, &sa, NULL) == -1) + *signals != SIGBUS) || sigaction(*signals, &sa, nullptr) == -1) { *signals = 0; retval = -1; @@ -502,7 +502,7 @@ static bool is_debugger_present() // Call sysctl. size = sizeof(info); - junk = sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0); + junk = sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, nullptr, 0); assert(junk == 0); // We're being debugged if the P_TRACED flag is set. @@ -516,7 +516,7 @@ void crashCatcherInstall(int argc, char **argv, const std::string &crashLogPath) if ((argc == 2 && strcmp(argv[1], "--cc-handle-crash") == 0) || !is_debugger_present()) { int s[5] = { SIGSEGV, SIGILL, SIGFPE, SIGBUS, SIGABRT }; - if (crashCatcherInstallHandlers(argc, argv, 5, s, crashLogPath.c_str(), NULL) == -1) + if (crashCatcherInstallHandlers(argc, argv, 5, s, crashLogPath.c_str(), nullptr) == -1) { Log(Debug::Warning) << "Installing crash handler failed"; } diff --git a/components/debug/debugging.cpp b/components/debug/debugging.cpp index a4c59c221..e89a65956 100644 --- a/components/debug/debugging.cpp +++ b/components/debug/debugging.cpp @@ -101,7 +101,7 @@ int wrapApplication(int (*innerApplication)(int argc, char *argv[]), int argc, c #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); + SDL_ShowSimpleMessageBox(0, (appName + ": Fatal error").c_str(), e.what(), nullptr); Log(Debug::Error) << "Error: " << e.what(); diff --git a/components/esm/esmreader.cpp b/components/esm/esmreader.cpp index 67b9d6a38..a6b947dd3 100644 --- a/components/esm/esmreader.cpp +++ b/components/esm/esmreader.cpp @@ -23,8 +23,8 @@ ESMReader::ESMReader() : mIdx(0) , mRecordFlags(0) , mBuffer(50*1024) - , mGlobalReaderList(NULL) - , mEncoder(NULL) + , mGlobalReaderList(nullptr) + , mEncoder(nullptr) , mFileSize(0) { } diff --git a/components/esm/esmwriter.cpp b/components/esm/esmwriter.cpp index b8c78a2b9..09fca4b26 100644 --- a/components/esm/esmwriter.cpp +++ b/components/esm/esmwriter.cpp @@ -10,9 +10,9 @@ namespace ESM { ESMWriter::ESMWriter() : mRecords() - , mStream(NULL) + , mStream(nullptr) , mHeaderPos() - , mEncoder(NULL) + , mEncoder(nullptr) , mRecordCount(0) , mCounting(true) , mHeader() diff --git a/components/esm/loadland.cpp b/components/esm/loadland.cpp index a91dfe3d3..9e5e9d07e 100644 --- a/components/esm/loadland.cpp +++ b/components/esm/loadland.cpp @@ -16,7 +16,7 @@ namespace ESM , mY(0) , mPlugin(0) , mDataTypes(0) - , mLandData(NULL) + , mLandData(nullptr) { } @@ -73,7 +73,7 @@ namespace ESM mContext = esm.getContext(); - mLandData = NULL; + mLandData = nullptr; // Skip the land data here. Load it when the cell is loaded. while (esm.hasMoreSubs()) @@ -298,7 +298,7 @@ namespace ESM if (mLandData) { delete mLandData; - mLandData = NULL; + mLandData = nullptr; } } diff --git a/components/esm/loadland.hpp b/components/esm/loadland.hpp index eaf766442..b4b66c601 100644 --- a/components/esm/loadland.hpp +++ b/components/esm/loadland.hpp @@ -129,9 +129,9 @@ struct Land /** * Actually loads data into target - * If target is NULL, assumed target is mLandData + * If target is nullptr, assumed target is mLandData */ - void loadData(int flags, LandData* target = NULL) const; + void loadData(int flags, LandData* target = nullptr) const; /** * Frees memory allocated for mLandData diff --git a/components/esmterrain/storage.cpp b/components/esmterrain/storage.cpp index 0fa28cf0b..dfdc9718d 100644 --- a/components/esmterrain/storage.cpp +++ b/components/esmterrain/storage.cpp @@ -50,7 +50,7 @@ namespace ESMTerrain const ESM::Land::LandData *LandObject::getData(int flags) const { if ((mData.mDataLoaded & flags) != flags) - return NULL; + return nullptr; return &mData; } diff --git a/components/files/androidpath.cpp b/components/files/androidpath.cpp index 84886f473..bf5c98577 100644 --- a/components/files/androidpath.cpp +++ b/components/files/androidpath.cpp @@ -40,15 +40,15 @@ namespace boost::filesystem::path getUserHome() { const char* dir = getenv("HOME"); - if (dir == NULL) + if (dir == nullptr) { struct passwd* pwd = getpwuid(getuid()); - if (pwd != NULL) + if (pwd != nullptr) { dir = pwd->pw_dir; } } - if (dir == NULL) + if (dir == nullptr) return boost::filesystem::path(); else return boost::filesystem::path(dir); diff --git a/components/files/linuxpath.cpp b/components/files/linuxpath.cpp index 1f6a3d913..3743eef4c 100644 --- a/components/files/linuxpath.cpp +++ b/components/files/linuxpath.cpp @@ -14,15 +14,15 @@ namespace boost::filesystem::path getUserHome() { const char* dir = getenv("HOME"); - if (dir == NULL) + if (dir == nullptr) { struct passwd* pwd = getpwuid(getuid()); - if (pwd != NULL) + if (pwd != nullptr) { dir = pwd->pw_dir; } } - if (dir == NULL) + if (dir == nullptr) return boost::filesystem::path(); else return boost::filesystem::path(dir); diff --git a/components/files/lowlevelfile.cpp b/components/files/lowlevelfile.cpp index 169a6f813..aff15777f 100644 --- a/components/files/lowlevelfile.cpp +++ b/components/files/lowlevelfile.cpp @@ -21,22 +21,22 @@ LowLevelFile::LowLevelFile () { - mHandle = NULL; + mHandle = nullptr; } LowLevelFile::~LowLevelFile () { - if (mHandle != NULL) + if (mHandle != nullptr) fclose (mHandle); } void LowLevelFile::open (char const * filename) { - assert (mHandle == NULL); + assert (mHandle == nullptr); mHandle = fopen (filename, "rb"); - if (mHandle == NULL) + if (mHandle == nullptr) { std::ostringstream os; os << "Failed to open '" << filename << "' for reading."; @@ -46,16 +46,16 @@ void LowLevelFile::open (char const * filename) void LowLevelFile::close () { - assert (mHandle != NULL); + assert (mHandle != nullptr); fclose (mHandle); - mHandle = NULL; + mHandle = nullptr; } size_t LowLevelFile::size () { - assert (mHandle != NULL); + assert (mHandle != nullptr); long oldPosition = ftell (mHandle); @@ -78,7 +78,7 @@ size_t LowLevelFile::size () void LowLevelFile::seek (size_t Position) { - assert (mHandle != NULL); + assert (mHandle != nullptr); if (fseek (mHandle, Position, SEEK_SET) != 0) throw std::runtime_error ("A seek operation on a file failed."); @@ -86,7 +86,7 @@ void LowLevelFile::seek (size_t Position) size_t LowLevelFile::tell () { - assert (mHandle != NULL); + assert (mHandle != nullptr); long Position = ftell (mHandle); @@ -98,7 +98,7 @@ size_t LowLevelFile::tell () size_t LowLevelFile::read (void * data, size_t size) { - assert (mHandle != NULL); + assert (mHandle != nullptr); int amount = fread (data, 1, size, mHandle); @@ -296,7 +296,7 @@ void LowLevelFile::seek (size_t Position) { assert (mHandle != INVALID_HANDLE_VALUE); - if (SetFilePointer (mHandle, Position, NULL, SEEK_SET) == INVALID_SET_FILE_POINTER) + if (SetFilePointer (mHandle, Position, nullptr, SEEK_SET) == INVALID_SET_FILE_POINTER) if (GetLastError () != NO_ERROR) throw std::runtime_error ("A seek operation on a file failed."); } @@ -305,7 +305,7 @@ size_t LowLevelFile::tell () { assert (mHandle != INVALID_HANDLE_VALUE); - DWORD value = SetFilePointer (mHandle, 0, NULL, SEEK_CUR); + DWORD value = SetFilePointer (mHandle, 0, nullptr, SEEK_CUR); if (value == INVALID_SET_FILE_POINTER && GetLastError () != NO_ERROR) throw std::runtime_error ("A query operation on a file failed."); @@ -319,7 +319,7 @@ size_t LowLevelFile::read (void * data, size_t size) DWORD read; - if (!ReadFile (mHandle, data, size, &read, NULL)) + if (!ReadFile (mHandle, data, size, &read, nullptr)) throw std::runtime_error ("A read operation on a file failed."); return read; diff --git a/components/files/macospath.cpp b/components/files/macospath.cpp index b49b72e46..32c35c98f 100644 --- a/components/files/macospath.cpp +++ b/components/files/macospath.cpp @@ -14,15 +14,15 @@ namespace boost::filesystem::path getUserHome() { const char* dir = getenv("HOME"); - if (dir == NULL) + if (dir == nullptr) { struct passwd* pwd = getpwuid(getuid()); - if (pwd != NULL) + if (pwd != nullptr) { dir = pwd->pw_dir; } } - if (dir == NULL) + if (dir == nullptr) return boost::filesystem::path(); else return boost::filesystem::path(dir); diff --git a/components/files/windowspath.cpp b/components/files/windowspath.cpp index 4b7c6c50a..2354e6f31 100644 --- a/components/files/windowspath.cpp +++ b/components/files/windowspath.cpp @@ -42,7 +42,7 @@ boost::filesystem::path WindowsPath::getUserConfigPath() const WCHAR path[MAX_PATH + 1]; memset(path, 0, sizeof(path)); - if(SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_PERSONAL | CSIDL_FLAG_CREATE, NULL, 0, path))) + if(SUCCEEDED(SHGetFolderPathW(nullptr, CSIDL_PERSONAL | CSIDL_FLAG_CREATE, nullptr, 0, path))) { userPath = boost::filesystem::path(bconv::utf_to_utf(path)); } @@ -63,7 +63,7 @@ boost::filesystem::path WindowsPath::getGlobalConfigPath() const WCHAR path[MAX_PATH + 1]; memset(path, 0, sizeof(path)); - if(SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_PROGRAM_FILES | CSIDL_FLAG_CREATE, NULL, 0, path))) + if(SUCCEEDED(SHGetFolderPathW(nullptr, CSIDL_PROGRAM_FILES | CSIDL_FLAG_CREATE, nullptr, 0, path))) { globalPath = boost::filesystem::path(bconv::utf_to_utf(path)); } @@ -99,7 +99,7 @@ boost::filesystem::path WindowsPath::getInstallPath() const std::vector buf(512); int len = 512; - if (RegQueryValueEx(hKey, TEXT("Installed Path"), NULL, NULL, (LPBYTE)&buf[0], (LPDWORD)&len) == ERROR_SUCCESS) + if (RegQueryValueEx(hKey, TEXT("Installed Path"), nullptr, nullptr, (LPBYTE)&buf[0], (LPDWORD)&len) == ERROR_SUCCESS) { installPath = &buf[0]; } diff --git a/components/myguiplatform/additivelayer.cpp b/components/myguiplatform/additivelayer.cpp index aa2ef08b3..49558cf8e 100644 --- a/components/myguiplatform/additivelayer.cpp +++ b/components/myguiplatform/additivelayer.cpp @@ -27,7 +27,7 @@ namespace osgMyGUI MyGUI::OverlappedLayer::renderToTarget(_target, _update); - renderManager.setInjectState(NULL); + renderManager.setInjectState(nullptr); } } diff --git a/components/myguiplatform/myguidatamanager.cpp b/components/myguiplatform/myguidatamanager.cpp index ddd7ca342..c90e09221 100644 --- a/components/myguiplatform/myguidatamanager.cpp +++ b/components/myguiplatform/myguidatamanager.cpp @@ -24,7 +24,7 @@ MyGUI::IDataStream *DataManager::getData(const std::string &name) if (stream->fail()) { Log(Debug::Error) << "DataManager::getData: Failed to open '" << name << "'"; - return NULL; + return nullptr; } return new MyGUI::DataFileStream(stream.release()); } diff --git a/components/myguiplatform/myguirendermanager.cpp b/components/myguiplatform/myguirendermanager.cpp index 5ab7ab736..845d0c484 100644 --- a/components/myguiplatform/myguirendermanager.cpp +++ b/components/myguiplatform/myguirendermanager.cpp @@ -52,7 +52,7 @@ public: { public: FrameUpdate() - : mRenderManager(NULL) + : mRenderManager(nullptr) { } @@ -76,7 +76,7 @@ public: { public: CollectDrawCalls() - : mRenderManager(NULL) + : mRenderManager(nullptr) { } @@ -134,9 +134,9 @@ public: { state->bindVertexBufferObject(bufferobject); - glVertexPointer(3, GL_FLOAT, sizeof(MyGUI::Vertex), (char*)NULL); - glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(MyGUI::Vertex), (char*)NULL + 12); - glTexCoordPointer(2, GL_FLOAT, sizeof(MyGUI::Vertex), (char*)NULL + 16); + glVertexPointer(3, GL_FLOAT, sizeof(MyGUI::Vertex), (char*)nullptr); + glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(MyGUI::Vertex), (char*)nullptr + 12); + glTexCoordPointer(2, GL_FLOAT, sizeof(MyGUI::Vertex), (char*)nullptr + 16); } else { @@ -355,7 +355,7 @@ RenderManager::RenderManager(osgViewer::Viewer *viewer, osg::Group *sceneroot, R , mUpdate(false) , mIsInitialise(false) , mInvScalingFactor(1.f) - , mInjectState(NULL) + , mInjectState(nullptr) { if (scalingFactor != 0.f) mInvScalingFactor = 1.f / scalingFactor; @@ -537,7 +537,7 @@ void RenderManager::destroyTexture(MyGUI::ITexture *texture) MyGUI::ITexture* RenderManager::getTexture(const std::string &name) { if (name.empty()) - return NULL; + return nullptr; MapTexture::const_iterator item = mTextures.find(name); if(item == mTextures.end()) diff --git a/components/myguiplatform/myguirendermanager.hpp b/components/myguiplatform/myguirendermanager.hpp index 4a0aae3cd..8c9e3e30f 100644 --- a/components/myguiplatform/myguirendermanager.hpp +++ b/components/myguiplatform/myguirendermanager.hpp @@ -98,7 +98,7 @@ public: /** @see IRenderTarget::doRender */ virtual void doRender(MyGUI::IVertexBuffer *buffer, MyGUI::ITexture *texture, size_t count); - /** specify a StateSet to inject for rendering. The StateSet will be used by future doRender calls until you reset it to NULL again. */ + /** specify a StateSet to inject for rendering. The StateSet will be used by future doRender calls until you reset it to nullptr again. */ void setInjectState(osg::StateSet* stateSet); /** @see IRenderTarget::getInfo */ diff --git a/components/myguiplatform/myguitexture.cpp b/components/myguiplatform/myguitexture.cpp index 6c53a9699..756893974 100644 --- a/components/myguiplatform/myguitexture.cpp +++ b/components/myguiplatform/myguitexture.cpp @@ -23,7 +23,7 @@ namespace osgMyGUI } OSGTexture::OSGTexture(osg::Texture2D *texture) - : mImageManager(NULL) + : mImageManager(nullptr) , mTexture(texture) , mFormat(MyGUI::PixelFormat::Unknow) , mUsage(MyGUI::TextureUsage::Default) diff --git a/components/nif/niffile.cpp b/components/nif/niffile.cpp index 4061247b5..66bbfdb65 100644 --- a/components/nif/niffile.cpp +++ b/components/nif/niffile.cpp @@ -162,7 +162,7 @@ void NIFFile::parse(Files::IStreamPtr stream) for(size_t i = 0;i < recNum;i++) { - Record *r = NULL; + Record *r = nullptr; std::string rec = nif.getString(); if(rec.empty()) @@ -182,7 +182,7 @@ void NIFFile::parse(Files::IStreamPtr stream) else fail("Unknown record type " + rec); - assert(r != NULL); + assert(r != nullptr); assert(r->recType != RC_MISSING); r->recName = rec; r->recIndex = i; @@ -203,7 +203,7 @@ void NIFFile::parse(Files::IStreamPtr stream) } else { - roots[i] = NULL; + roots[i] = nullptr; warn("Null Root found"); } } diff --git a/components/nif/node.hpp b/components/nif/node.hpp index c32969d1b..d9afbbed7 100644 --- a/components/nif/node.hpp +++ b/components/nif/node.hpp @@ -53,7 +53,7 @@ public: boundXYZ = nif->getVector3(); } - parent = NULL; + parent = nullptr; isBone = false; } @@ -64,7 +64,7 @@ public: props.post(nif); } - // Parent node, or NULL for the root node. As far as I'm aware, only + // Parent node, or nullptr for the root node. As far as I'm aware, only // NiNodes (or types derived from NiNodes) can be parents. NiNode *parent; diff --git a/components/nif/recordptr.hpp b/components/nif/recordptr.hpp index 09c380987..e8aa8cb5b 100644 --- a/components/nif/recordptr.hpp +++ b/components/nif/recordptr.hpp @@ -40,25 +40,25 @@ public: void post(NIFFile *nif) { if(index < 0) - ptr = NULL; + ptr = nullptr; else { Record *r = nif->getRecord(index); // And cast it ptr = dynamic_cast(r); - assert(ptr != NULL); + assert(ptr != nullptr); } } /// Look up the actual object from the index const X* getPtr() const { - assert(ptr != NULL); + assert(ptr != nullptr); return ptr; } X* getPtr() { - assert(ptr != NULL); + assert(ptr != nullptr); return ptr; } @@ -75,7 +75,7 @@ public: /// Pointers are allowed to be empty bool empty() const - { return ptr == NULL; } + { return ptr == nullptr; } }; /** A list of references to other records. These are read as a list, diff --git a/components/nifbullet/bulletnifloader.cpp b/components/nifbullet/bulletnifloader.cpp index 5239e93fc..7b206e40c 100644 --- a/components/nifbullet/bulletnifloader.cpp +++ b/components/nifbullet/bulletnifloader.cpp @@ -25,7 +25,7 @@ namespace osg::Matrixf getWorldTransform(const Nif::Node *node) { - if(node->parent != NULL) + if(node->parent != nullptr) return node->trafo.toMatrix() * getWorldTransform(node->parent); return node->trafo.toMatrix(); } @@ -61,8 +61,8 @@ osg::ref_ptr BulletNifLoader::load(const Nif::File& nif) { mShape = new Resource::BulletShape; - mCompoundShape = NULL; - mStaticMesh = NULL; + mCompoundShape = nullptr; + mStaticMesh = nullptr; if (nif.numRoots() < 1) { @@ -71,10 +71,10 @@ osg::ref_ptr BulletNifLoader::load(const Nif::File& nif) } Nif::Record *r = nif.getRoot(0); - assert(r != NULL); + assert(r != nullptr); Nif::Node *node = dynamic_cast(r); - if (node == NULL) + if (node == nullptr) { warn("First root in file was not a node, but a " + r->recName + ". Skipping file."); @@ -216,7 +216,7 @@ void BulletNifLoader::handleNode(const std::string& fileName, const Nif::Node *n { // Get the next extra data in the list e = e->extra.getPtr(); - assert(e != NULL); + assert(e != nullptr); if (e->recType == Nif::RC_NiStringExtraData) { @@ -263,7 +263,7 @@ void BulletNifLoader::handleNode(const std::string& fileName, const Nif::Node *n void BulletNifLoader::handleNiTriShape(const Nif::NiTriShape *shape, int flags, const osg::Matrixf &transform, bool isAnimated) { - assert(shape != NULL); + assert(shape != nullptr); // If the object was marked "NCO" earlier, it shouldn't collide with // anything. So don't do anything. diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 3198e995c..eb20b7702 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -200,7 +200,7 @@ namespace NifOsg } const Nif::Record *r = nif->getRoot(0); - assert(r != NULL); + assert(r != nullptr); if(r->recType != Nif::RC_NiSequenceStreamHelper) { @@ -257,12 +257,12 @@ namespace NifOsg const Nif::Record* r = nif->getRoot(0); const Nif::Node* nifNode = dynamic_cast(r); - if (nifNode == NULL) + if (nifNode == nullptr) nif->fail("First root was not a node, but a " + r->recName); osg::ref_ptr textkeys (new TextKeyMapHolder); - osg::ref_ptr created = handleNode(nifNode, NULL, imageManager, std::vector(), 0, false, false, false, &textkeys->mTextKeys); + osg::ref_ptr created = handleNode(nifNode, nullptr, imageManager, std::vector(), 0, false, false, false, &textkeys->mTextKeys); if (nif->getUseSkinning()) { @@ -297,7 +297,7 @@ namespace NifOsg { // Get the lowest numbered recIndex of the NiTexturingProperty root node. // This is what is overridden when a spell effect "particle texture" is used. - if (nifNode->parent == NULL && !mFoundFirstRootTexturingProperty && props[i].getPtr()->recType == Nif::RC_NiTexturingProperty) + if (nifNode->parent == nullptr && !mFoundFirstRootTexturingProperty && props[i].getPtr()->recType == Nif::RC_NiTexturingProperty) { mFirstRootTextureIndex = props[i].getPtr()->recIndex; mFoundFirstRootTexturingProperty = true; @@ -338,7 +338,7 @@ namespace NifOsg osg::ref_ptr handleSourceTexture(const Nif::NiSourceTexture* st, Resource::ImageManager* imageManager) { if (!st) - return NULL; + return nullptr; osg::ref_ptr image; if (!st->external && !st->data.empty()) @@ -464,10 +464,10 @@ namespace NifOsg } osg::ref_ptr handleNode(const Nif::Node* nifNode, osg::Group* parentNode, Resource::ImageManager* imageManager, - std::vector boundTextures, int animflags, bool skipMeshes, bool hasMarkers, bool isAnimated, TextKeyMap* textKeys, osg::Node* rootNode=NULL) + std::vector boundTextures, int animflags, bool skipMeshes, bool hasMarkers, bool isAnimated, TextKeyMap* textKeys, osg::Node* rootNode=nullptr) { - if (rootNode != NULL && Misc::StringUtils::ciEqual(nifNode->name, "Bounding Box")) - return NULL; + if (rootNode != nullptr && Misc::StringUtils::ciEqual(nifNode->name, "Bounding Box")) + return nullptr; osg::ref_ptr node = createNode(nifNode); @@ -791,7 +791,7 @@ namespace NifOsg // Load the initial state of the particle system, i.e. the initial particles and their positions, velocity and colors. void handleParticleInitialState(const Nif::Node* nifNode, osgParticle::ParticleSystem* partsys, const Nif::NiParticleSystemController* partctrl) { - const Nif::NiAutoNormalParticlesData *particledata = NULL; + const Nif::NiAutoNormalParticlesData *particledata = nullptr; if(nifNode->recType == Nif::RC_NiAutoNormalParticles) particledata = static_cast(nifNode)->data.getPtr(); else if(nifNode->recType == Nif::RC_NiRotatingParticles) @@ -873,7 +873,7 @@ namespace NifOsg osg::ref_ptr partsys (new ParticleSystem); partsys->setSortMode(osgParticle::ParticleSystem::SORT_BACK_TO_FRONT); - const Nif::NiParticleSystemController* partctrl = NULL; + const Nif::NiParticleSystemController* partctrl = nullptr; for (Nif::ControllerPtr ctrl = nifNode->controller; !ctrl.empty(); ctrl = ctrl->next) { if (!(ctrl->flags & Nif::NiNode::ControllerFlag_Active)) @@ -1193,11 +1193,11 @@ namespace NifOsg break; default: Log(Debug::Info) << "Unhandled internal pixel format " << pixelData->fmt << " in " << mFilename; - return NULL; + return nullptr; } if (pixelData->mipmaps.empty()) - return NULL; + return nullptr; int width = 0; int height = 0; @@ -1211,7 +1211,7 @@ namespace NifOsg if (mipSize + mip.dataOffset > pixelData->data.size()) { Log(Debug::Info) << "Internal texture's mipmap data out of bounds, ignoring texture"; - return NULL; + return nullptr; } if (i != 0) @@ -1226,7 +1226,7 @@ namespace NifOsg if (width <= 0 || height <= 0) { Log(Debug::Info) << "Internal Texture Width and height must be non zero, ignoring texture"; - return NULL; + return nullptr; } unsigned char* data = new unsigned char[pixelData->data.size()]; diff --git a/components/nifosg/particle.cpp b/components/nifosg/particle.cpp index 03e836a0f..f5c055a15 100644 --- a/components/nifosg/particle.cpp +++ b/components/nifosg/particle.cpp @@ -39,7 +39,7 @@ osgParticle::Particle* ParticleSystem::createParticle(const osgParticle::Particl { if (numParticles()-numDeadParticles() < mQuota) return osgParticle::ParticleSystem::createParticle(ptemplate); - return NULL; + return nullptr; } void InverseWorldMatrix::operator()(osg::Node *node, osg::NodeVisitor *nv) @@ -312,7 +312,7 @@ void Emitter::emitParticles(double dt) FindGroupByRecIndex::FindGroupByRecIndex(int recIndex) : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) - , mFound(NULL) + , mFound(nullptr) , mRecIndex(recIndex) { } diff --git a/components/resource/bulletshape.cpp b/components/resource/bulletshape.cpp index dbdbf0c6e..15a747dd3 100644 --- a/components/resource/bulletshape.cpp +++ b/components/resource/bulletshape.cpp @@ -12,7 +12,7 @@ namespace Resource { BulletShape::BulletShape() - : mCollisionShape(NULL) + : mCollisionShape(nullptr) { } @@ -32,7 +32,7 @@ BulletShape::~BulletShape() void BulletShape::deleteShape(btCollisionShape* shape) { - if(shape!=NULL) + if(shape!=nullptr) { if(shape->isCompound()) { diff --git a/components/resource/bulletshapemanager.cpp b/components/resource/bulletshapemanager.cpp index 622506d6b..4193cd5b4 100644 --- a/components/resource/bulletshapemanager.cpp +++ b/components/resource/bulletshapemanager.cpp @@ -24,7 +24,7 @@ namespace Resource struct GetTriangleFunctor { GetTriangleFunctor() - : mTriMesh(NULL) + : mTriMesh(nullptr) { } diff --git a/components/resource/multiobjectcache.hpp b/components/resource/multiobjectcache.hpp index 527247bf9..b73e29d50 100644 --- a/components/resource/multiobjectcache.hpp +++ b/components/resource/multiobjectcache.hpp @@ -30,7 +30,7 @@ namespace Resource void addEntryToObjectCache(const std::string& filename, osg::Object* object); - /** Take an Object from cache. Return NULL if no object found. */ + /** Take an Object from cache. Return nullptr if no object found. */ osg::ref_ptr takeFromObjectCache(const std::string& fileName); /** call releaseGLObjects on all objects attached to the object cache.*/ diff --git a/components/sceneutil/clone.cpp b/components/sceneutil/clone.cpp index 08f36cfcf..99dd7bad3 100644 --- a/components/sceneutil/clone.cpp +++ b/components/sceneutil/clone.cpp @@ -26,7 +26,7 @@ namespace SceneUtil osg::StateSet* CopyOp::operator ()(const osg::StateSet* stateset) const { if (!stateset) - return NULL; + return nullptr; if (stateset->getDataVariance() == osg::StateSet::DYNAMIC) return osg::clone(stateset, *this); return const_cast(stateset); diff --git a/components/sceneutil/controller.cpp b/components/sceneutil/controller.cpp index 8fef00333..dfc72918a 100644 --- a/components/sceneutil/controller.cpp +++ b/components/sceneutil/controller.cpp @@ -19,7 +19,7 @@ namespace SceneUtil bool Controller::hasInput() const { - return mSource.get() != NULL; + return mSource.get() != nullptr; } float Controller::getInputValue(osg::NodeVisitor* nv) diff --git a/components/sceneutil/lightmanager.cpp b/components/sceneutil/lightmanager.cpp index 018abeefc..e102653b9 100644 --- a/components/sceneutil/lightmanager.cpp +++ b/components/sceneutil/lightmanager.cpp @@ -102,7 +102,7 @@ namespace SceneUtil if (LightManager* lightManager = dynamic_cast(path[i])) return lightManager; } - return NULL; + return nullptr; } // Set on a LightSource. Adds the light source to its light manager for the current frame. @@ -299,7 +299,7 @@ namespace SceneUtil virtual osg::Object* cloneType() const { return new DisableLight(mIndex); } virtual osg::Object* clone(const osg::CopyOp& copyop) const { return new DisableLight(*this,copyop); } - virtual bool isSameKindAs(const osg::Object* obj) const { return dynamic_cast(obj)!=NULL; } + virtual bool isSameKindAs(const osg::Object* obj) const { return dynamic_cast(obj)!=nullptr; } virtual const char* libraryName() const { return "SceneUtil"; } virtual const char* className() const { return "DisableLight"; } virtual Type getType() const { return LIGHT; } @@ -323,17 +323,17 @@ namespace SceneUtil virtual void apply(osg::State& state) const { int lightNum = GL_LIGHT0 + mIndex; - glLightfv( lightNum, GL_AMBIENT, mNull.ptr() ); - glLightfv( lightNum, GL_DIFFUSE, mNull.ptr() ); - glLightfv( lightNum, GL_SPECULAR, mNull.ptr() ); + glLightfv( lightNum, GL_AMBIENT, mnullptr.ptr() ); + glLightfv( lightNum, GL_DIFFUSE, mnullptr.ptr() ); + glLightfv( lightNum, GL_SPECULAR, mnullptr.ptr() ); LightStateCache* cache = getLightStateCache(state.getContextID()); - cache->lastAppliedLight[mIndex] = NULL; + cache->lastAppliedLight[mIndex] = nullptr; } private: unsigned int mIndex; - osg::Vec4f mNull; + osg::Vec4f mnullptr; }; void LightManager::setStartLight(int start) @@ -447,7 +447,7 @@ namespace SceneUtil { unsigned int maxLights = static_cast (8 - mLightManager->getStartLight()); - osg::StateSet* stateset = NULL; + osg::StateSet* stateset = nullptr; if (mLightList.size() > maxLights) { diff --git a/components/sceneutil/lightmanager.hpp b/components/sceneutil/lightmanager.hpp index 55d0c69cd..d35aa6058 100644 --- a/components/sceneutil/lightmanager.hpp +++ b/components/sceneutil/lightmanager.hpp @@ -158,7 +158,7 @@ namespace SceneUtil { public: LightListCallback() - : mLightManager(NULL) + : mLightManager(nullptr) , mLastFrameNumber(0) {} LightListCallback(const LightListCallback& copy, const osg::CopyOp& copyop) diff --git a/components/sceneutil/lightutil.cpp b/components/sceneutil/lightutil.cpp index 5ad9bd6dc..d44724e8f 100644 --- a/components/sceneutil/lightutil.cpp +++ b/components/sceneutil/lightutil.cpp @@ -45,7 +45,7 @@ namespace SceneUtil SceneUtil::FindByNameVisitor visitor("AttachLight"); node->accept(visitor); - osg::Group* attachTo = NULL; + osg::Group* attachTo = nullptr; if (visitor.mFoundNode) { attachTo = visitor.mFoundNode; diff --git a/components/sceneutil/optimizer.cpp b/components/sceneutil/optimizer.cpp index b86744599..5d50c369d 100644 --- a/components/sceneutil/optimizer.cpp +++ b/components/sceneutil/optimizer.cpp @@ -166,7 +166,7 @@ class CollectLowestTransformsVisitor : public BaseOptimizerVisitor } else { - // for all current objects mark a NULL transform for them. + // for all current objects mark a nullptr transform for them. registerWithCurrentObjects(0); } } @@ -826,7 +826,7 @@ void Optimizer::RemoveRedundantNodesVisitor::apply(osg::Transform& transform) isOperationPermissible(transform)) { osg::Matrix matrix; - transform.computeWorldToLocalMatrix(matrix,NULL); + transform.computeWorldToLocalMatrix(matrix,nullptr); if (matrix.isIdentity()) { _redundantNodeList.insert(&transform); @@ -1000,7 +1000,7 @@ struct LessGeometryPrimitiveType }; -/// Shortcut to get size of an array, even if pointer is NULL. +/// Shortcut to get size of an array, even if pointer is nullptr. inline unsigned int getSize(const osg::Array * a) { return a ? a->getNumElements() : 0; } /// When merging geometries, tests if two arrays can be merged, regarding to their number of components, and the number of vertices. @@ -1170,7 +1170,7 @@ bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group) MergeList::iterator eachMergeList=mergeListTmp.begin(); for(;eachMergeList!=mergeListTmp.end();++eachMergeList) { - if (!eachMergeList->empty() && eachMergeList->front()!=NULL + if (!eachMergeList->empty() && eachMergeList->front()!=nullptr && isAbleToMerge(*eachMergeList->front(),*geomToPush)) { eachMergeList->push_back(geomToPush); diff --git a/components/sceneutil/riggeometry.cpp b/components/sceneutil/riggeometry.cpp index c409bcd5c..30a3f076c 100644 --- a/components/sceneutil/riggeometry.cpp +++ b/components/sceneutil/riggeometry.cpp @@ -37,7 +37,7 @@ namespace SceneUtil { RigGeometry::RigGeometry() - : mSkeleton(NULL) + : mSkeleton(nullptr) , mLastFrameNumber(0) , mBoundsFirstFrame(true) { @@ -47,7 +47,7 @@ RigGeometry::RigGeometry() RigGeometry::RigGeometry(const RigGeometry ©, const osg::CopyOp ©op) : Drawable(copy, copyop) - , mSkeleton(NULL) + , mSkeleton(nullptr) , mInfluenceMap(copy.mInfluenceMap) , mLastFrameNumber(0) , mBoundsFirstFrame(true) @@ -98,7 +98,7 @@ void RigGeometry::setSourceGeometry(osg::ref_ptr sourceGeometry) to.setTexCoordArray(7, tangentArray, osg::Array::BIND_PER_VERTEX); } else - mSourceTangents = NULL; + mSourceTangents = nullptr; } } @@ -296,7 +296,7 @@ void RigGeometry::updateGeomToSkelMatrix(const osg::NodePath& nodePath) { if (!geomToSkelMatrix) geomToSkelMatrix = new osg::RefMatrix; - trans->computeWorldToLocalMatrix(*geomToSkelMatrix, NULL); + trans->computeWorldToLocalMatrix(*geomToSkelMatrix, nullptr); } } } diff --git a/components/sceneutil/skeleton.cpp b/components/sceneutil/skeleton.cpp index 58ed9a27c..9be440d93 100644 --- a/components/sceneutil/skeleton.cpp +++ b/components/sceneutil/skeleton.cpp @@ -64,7 +64,7 @@ Bone* Skeleton::getBone(const std::string &name) BoneCache::iterator found = mBoneCache.find(Misc::StringUtils::lowerCase(name)); if (found == mBoneCache.end()) - return NULL; + return nullptr; // find or insert in the bone hierarchy @@ -81,7 +81,7 @@ Bone* Skeleton::getBone(const std::string &name) if (!matrixTransform) continue; - Bone* child = NULL; + Bone* child = nullptr; for (unsigned int i=0; imChildren.size(); ++i) { if (bone->mChildren[i]->mNode == *it) @@ -117,7 +117,7 @@ void Skeleton::updateBoneMatrices(unsigned int traversalNumber) if (mRootBone.get()) { for (unsigned int i=0; imChildren.size(); ++i) - mRootBone->mChildren[i]->update(NULL); + mRootBone->mChildren[i]->update(nullptr); } mNeedToUpdateBoneMatrices = false; @@ -167,7 +167,7 @@ void Skeleton::childRemoved(unsigned int, unsigned int) } Bone::Bone() - : mNode(NULL) + : mNode(nullptr) { } diff --git a/components/sceneutil/statesetupdater.cpp b/components/sceneutil/statesetupdater.cpp index 5a5ecaded..121cdaca6 100644 --- a/components/sceneutil/statesetupdater.cpp +++ b/components/sceneutil/statesetupdater.cpp @@ -30,8 +30,8 @@ namespace SceneUtil void StateSetUpdater::reset() { - mStateSets[0] = NULL; - mStateSets[1] = NULL; + mStateSets[0] = nullptr; + mStateSets[1] = nullptr; } StateSetUpdater::StateSetUpdater() diff --git a/components/sceneutil/visitor.cpp b/components/sceneutil/visitor.cpp index 536331132..3c3559a08 100644 --- a/components/sceneutil/visitor.cpp +++ b/components/sceneutil/visitor.cpp @@ -101,7 +101,7 @@ namespace SceneUtil void CleanObjectRootVisitor::applyNode(osg::Node& node) { if (node.getStateSet()) - node.setStateSet(NULL); + node.setStateSet(nullptr); if (node.getNodeMask() == 0x1 && node.getNumParents() == 1) mToRemove.push_back(std::make_pair(&node, node.getParent(0))); diff --git a/components/sceneutil/visitor.hpp b/components/sceneutil/visitor.hpp index 3e9a565c0..02a351da7 100644 --- a/components/sceneutil/visitor.hpp +++ b/components/sceneutil/visitor.hpp @@ -9,14 +9,14 @@ namespace SceneUtil { // Find a Group by name, case-insensitive - // If not found, mFoundNode will be NULL + // If not found, mFoundNode will be nullptr class FindByNameVisitor : public osg::NodeVisitor { public: FindByNameVisitor(const std::string& nameToFind) : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) , mNameToFind(nameToFind) - , mFoundNode(NULL) + , mFoundNode(nullptr) { } diff --git a/components/sceneutil/workqueue.cpp b/components/sceneutil/workqueue.cpp index 37e8563e1..0ca5e4d3e 100644 --- a/components/sceneutil/workqueue.cpp +++ b/components/sceneutil/workqueue.cpp @@ -97,7 +97,7 @@ osg::ref_ptr WorkQueue::removeWorkItem() return item; } else - return NULL; + return nullptr; } unsigned int WorkQueue::getNumItems() const diff --git a/components/sceneutil/workqueue.hpp b/components/sceneutil/workqueue.hpp index 7de6a5af5..2084df328 100644 --- a/components/sceneutil/workqueue.hpp +++ b/components/sceneutil/workqueue.hpp @@ -57,7 +57,7 @@ namespace SceneUtil void addWorkItem(osg::ref_ptr item, bool front=false); /// Get the next work item from the front of the queue. If the queue is empty, waits until a new item is added. - /// If the workqueue is in the process of being destroyed, may return NULL. + /// If the workqueue is in the process of being destroyed, may return nullptr. /// @par Used internally by the WorkThread. osg::ref_ptr removeWorkItem(); diff --git a/components/sdlutil/sdlcursormanager.cpp b/components/sdlutil/sdlcursormanager.cpp index 1560b74b3..6d2117575 100644 --- a/components/sdlutil/sdlcursormanager.cpp +++ b/components/sdlutil/sdlcursormanager.cpp @@ -190,7 +190,7 @@ namespace CursorDecompression SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1"); SDL_Texture *cursorTexture = SDL_CreateTextureFromSurface(renderer, cursorSurface); - SDL_RenderCopyEx(renderer, cursorTexture, NULL, NULL, -rotDegrees, NULL, SDL_FLIP_VERTICAL); + SDL_RenderCopyEx(renderer, cursorTexture, nullptr, nullptr, -rotDegrees, nullptr, SDL_FLIP_VERTICAL); SDL_DestroyTexture(cursorTexture); SDL_FreeSurface(cursorSurface); diff --git a/components/sdlutil/sdlgraphicswindow.cpp b/components/sdlutil/sdlgraphicswindow.cpp index f9767238f..dc6129e43 100644 --- a/components/sdlutil/sdlgraphicswindow.cpp +++ b/components/sdlutil/sdlgraphicswindow.cpp @@ -77,7 +77,7 @@ void GraphicsWindowSDL2::init() return; WindowData *inheritedWindowData = dynamic_cast(_traits->inheritedWindowData.get()); - mWindow = inheritedWindowData ? inheritedWindowData->mWindow : NULL; + mWindow = inheritedWindowData ? inheritedWindowData->mWindow : nullptr; mOwnsWindow = (mWindow == 0); if(mOwnsWindow) @@ -162,7 +162,7 @@ bool GraphicsWindowSDL2::releaseContextImplementation() return false; } - return SDL_GL_MakeCurrent(NULL, NULL)==0; + return SDL_GL_MakeCurrent(nullptr, nullptr)==0; } @@ -170,11 +170,11 @@ void GraphicsWindowSDL2::closeImplementation() { if(mContext) SDL_GL_DeleteContext(mContext); - mContext = NULL; + mContext = nullptr; if(mWindow && mOwnsWindow) SDL_DestroyWindow(mWindow); - mWindow = NULL; + mWindow = nullptr; mValid = false; mRealized = false; diff --git a/components/sdlutil/sdlinputwrapper.cpp b/components/sdlutil/sdlinputwrapper.cpp index 5fc828fb5..f7111fdf8 100644 --- a/components/sdlutil/sdlinputwrapper.cpp +++ b/components/sdlutil/sdlinputwrapper.cpp @@ -10,10 +10,10 @@ namespace SDLUtil InputWrapper::InputWrapper(SDL_Window* window, osg::ref_ptr viewer, bool grab) : mSDLWindow(window), mViewer(viewer), - mMouseListener(NULL), - mKeyboardListener(NULL), - mWindowListener(NULL), - mConListener(NULL), + mMouseListener(nullptr), + mKeyboardListener(nullptr), + mWindowListener(nullptr), + mConListener(nullptr), mWarpX(0), mWarpY(0), mWarpCompensate(false), @@ -234,7 +234,7 @@ InputWrapper::InputWrapper(SDL_Window* window, osg::ref_ptr v bool InputWrapper::isKeyDown(SDL_Scancode key) { - return (SDL_GetKeyboardState(NULL)[key]) != 0; + return (SDL_GetKeyboardState(nullptr)[key]) != 0; } /// \brief Moves the mouse to the specified point within the viewport diff --git a/components/shader/shadermanager.cpp b/components/shader/shadermanager.cpp index 28f4300c2..f3d52f5f0 100644 --- a/components/shader/shadermanager.cpp +++ b/components/shader/shadermanager.cpp @@ -115,7 +115,7 @@ namespace Shader if (stream.fail()) { Log(Debug::Error) << "Failed to open " << p.string(); - return NULL; + return nullptr; } std::stringstream buffer; buffer << stream.rdbuf(); @@ -123,7 +123,7 @@ namespace Shader // parse includes std::string source = buffer.str(); if (!parseIncludes(boost::filesystem::path(mPath), source)) - return NULL; + return nullptr; templateIt = mShaderTemplates.insert(std::make_pair(shaderTemplate, source)).first; } @@ -136,7 +136,7 @@ namespace Shader { // Add to the cache anyway to avoid logging the same error over and over. mShaders.insert(std::make_pair(std::make_pair(shaderTemplate, defines), nullptr)); - return NULL; + return nullptr; } osg::ref_ptr shader (new osg::Shader(shaderType)); diff --git a/components/shader/shadermanager.hpp b/components/shader/shadermanager.hpp index bd820a725..6b2e02124 100644 --- a/components/shader/shadermanager.hpp +++ b/components/shader/shadermanager.hpp @@ -26,7 +26,7 @@ namespace Shader /// @param shaderTemplate The filename of the shader template. /// @param defines Define values that can be retrieved by the shader template. /// @param shaderType The type of shader (usually vertex or fragment shader). - /// @note May return NULL on failure. + /// @note May return nullptr on failure. /// @note Thread safe. osg::ref_ptr getShader(const std::string& shaderTemplate, const DefineMap& defines, osg::Shader::Type shaderType); diff --git a/components/shader/shadervisitor.cpp b/components/shader/shadervisitor.cpp index cbd950ea3..a0a6832d0 100644 --- a/components/shader/shadervisitor.cpp +++ b/components/shader/shadervisitor.cpp @@ -28,7 +28,7 @@ namespace Shader , mMaterialOverridden(false) , mNormalHeight(false) , mTexStageRequiringTangents(-1) - , mNode(NULL) + , mNode(nullptr) { } @@ -102,15 +102,15 @@ namespace Shader void ShaderVisitor::applyStateSet(osg::ref_ptr stateset, osg::Node& node) { - osg::StateSet* writableStateSet = NULL; + osg::StateSet* writableStateSet = nullptr; if (mAllowedToModifyStateSets) writableStateSet = node.getStateSet(); const osg::StateSet::TextureAttributeList& texAttributes = stateset->getTextureAttributeList(); if (!texAttributes.empty()) { - const osg::Texture* diffuseMap = NULL; - const osg::Texture* normalMap = NULL; - const osg::Texture* specularMap = NULL; + const osg::Texture* diffuseMap = nullptr; + const osg::Texture* normalMap = nullptr; + const osg::Texture* specularMap = nullptr; for(unsigned int unit=0;unitgetTextureAttribute(unit, osg::StateAttribute::TEXTURE); @@ -153,7 +153,7 @@ namespace Shader } } - if (mAutoUseNormalMaps && diffuseMap != NULL && normalMap == NULL && diffuseMap->getImage(0)) + if (mAutoUseNormalMaps && diffuseMap != nullptr && normalMap == nullptr && diffuseMap->getImage(0)) { std::string normalMapFileName = diffuseMap->getImage(0)->getFileName(); @@ -195,7 +195,7 @@ namespace Shader mRequirements.back().mNormalHeight = normalHeight; } } - if (mAutoUseSpecularMaps && diffuseMap != NULL && specularMap == NULL && diffuseMap->getImage(0)) + if (mAutoUseSpecularMaps && diffuseMap != nullptr && specularMap == nullptr && diffuseMap->getImage(0)) { std::string specularMapFileName = diffuseMap->getImage(0)->getFileName(); boost::replace_last(specularMapFileName, ".", mSpecularMapPattern + "."); @@ -254,7 +254,7 @@ namespace Shader return; osg::Node& node = *reqs.mNode; - osg::StateSet* writableStateSet = NULL; + osg::StateSet* writableStateSet = nullptr; if (mAllowedToModifyStateSets) writableStateSet = node.getOrCreateStateSet(); else @@ -321,7 +321,7 @@ namespace Shader // make sure that all UV sets are there for (std::map::const_iterator it = reqs.mTextures.begin(); it != reqs.mTextures.end(); ++it) { - if (sourceGeometry.getTexCoordArray(it->first) == NULL) + if (sourceGeometry.getTexCoordArray(it->first) == nullptr) { sourceGeometry.setTexCoordArray(it->first, sourceGeometry.getTexCoordArray(0)); changed = true; @@ -342,7 +342,7 @@ namespace Shader void ShaderVisitor::apply(osg::Geometry& geometry) { - bool needPop = (geometry.getStateSet() != NULL); + bool needPop = (geometry.getStateSet() != nullptr); if (geometry.getStateSet()) // TODO: check if stateset affects shader permutation before pushing it { pushRequirements(geometry); @@ -365,7 +365,7 @@ namespace Shader void ShaderVisitor::apply(osg::Drawable& drawable) { // non-Geometry drawable (e.g. particle system) - bool needPop = (drawable.getStateSet() != NULL); + bool needPop = (drawable.getStateSet() != nullptr); if (drawable.getStateSet()) { diff --git a/components/terrain/chunkmanager.cpp b/components/terrain/chunkmanager.cpp index 7575113ef..b23b0b76c 100644 --- a/components/terrain/chunkmanager.cpp +++ b/components/terrain/chunkmanager.cpp @@ -22,7 +22,7 @@ namespace Terrain { ChunkManager::ChunkManager(Storage *storage, Resource::SceneManager *sceneMgr, TextureManager* textureManager, CompositeMapRenderer* renderer) - : ResourceManager(NULL) + : ResourceManager(nullptr) , mStorage(storage) , mSceneManager(sceneMgr) , mTextureManager(textureManager) diff --git a/components/terrain/compositemaprenderer.cpp b/components/terrain/compositemaprenderer.cpp index d3fdfa1f8..3dc0aa41c 100644 --- a/components/terrain/compositemaprenderer.cpp +++ b/components/terrain/compositemaprenderer.cpp @@ -45,7 +45,7 @@ void CompositeMapRenderer::drawImplementation(osg::RenderInfo &renderInfo) const CompositeMap* node = *mImmediateCompileSet.begin(); mCompiled.insert(node); - compile(*node, renderInfo, NULL); + compile(*node, renderInfo, nullptr); mImmediateCompileSet.erase(mImmediateCompileSet.begin()); } diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp index f4fc8df89..2222cbb84 100644 --- a/components/terrain/quadtreenode.cpp +++ b/components/terrain/quadtreenode.cpp @@ -41,7 +41,7 @@ bool adjacent(ChildDirection dir, Direction dir2) QuadTreeNode* searchNeighbour (QuadTreeNode* currentNode, Direction dir) { if (currentNode->getDirection() == Root) - return NULL; // Arrived at root node, the root node does not have neighbours + return nullptr; // Arrived at root node, the root node does not have neighbours QuadTreeNode* nextNode; if (adjacent(currentNode->getDirection(), dir)) @@ -52,7 +52,7 @@ QuadTreeNode* searchNeighbour (QuadTreeNode* currentNode, Direction dir) if (nextNode && nextNode->getNumChildren()) return nextNode->getChild(reflect(currentNode->getDirection(), dir)); else - return NULL; + return nullptr; } QuadTreeNode::QuadTreeNode(QuadTreeNode* parent, ChildDirection direction, float size, const osg::Vec2f& center) diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index 9b9bbccd6..40f7a6fb0 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -96,8 +96,8 @@ class RootNode : public QuadTreeNode { public: RootNode(float size, const osg::Vec2f& center) - : QuadTreeNode(NULL, Root, size, center) - , mWorld(NULL) + : QuadTreeNode(nullptr, Root, size, center) + , mWorld(nullptr) { } @@ -317,7 +317,7 @@ void loadRenderingNode(ViewData::Entry& entry, ViewData* vd, ChunkManager* chunk unsigned int lodFlags = getLodFlags(entry.mNode, ourLod, vd); if (lodFlags != entry.mLodFlags) { - entry.mRenderingNode = NULL; + entry.mRenderingNode = nullptr; entry.mLodFlags = lodFlags; } } @@ -367,7 +367,7 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv) if (udc && udc->getUserData()) { mCompositeMapRenderer->setImmediate(static_cast(udc->getUserData())); - udc->setUserData(NULL); + udc->setUserData(nullptr); } entry.mRenderingNode->accept(nv); } @@ -430,7 +430,7 @@ void QuadTreeWorld::preload(View *view, const osg::Vec3f &eyePoint) ensureQuadTreeBuilt(); ViewData* vd = static_cast(view); - traverse(mRootNode.get(), vd, NULL, mRootNode->getLodCallback(), eyePoint, false); + traverse(mRootNode.get(), vd, nullptr, mRootNode->getLodCallback(), eyePoint, false); for (unsigned int i=0; igetNumEntries(); ++i) { diff --git a/components/terrain/terraindrawable.hpp b/components/terrain/terraindrawable.hpp index 79a28deeb..6bef60bc7 100644 --- a/components/terrain/terraindrawable.hpp +++ b/components/terrain/terraindrawable.hpp @@ -24,7 +24,7 @@ namespace Terrain public: virtual osg::Object* cloneType() const { return new TerrainDrawable (); } virtual osg::Object* clone(const osg::CopyOp& copyop) const { return new TerrainDrawable (*this,copyop); } - virtual bool isSameKindAs(const osg::Object* obj) const { return dynamic_cast(obj)!=NULL; } + virtual bool isSameKindAs(const osg::Object* obj) const { return dynamic_cast(obj)!=nullptr; } virtual const char* className() const { return "TerrainDrawable"; } virtual const char* libraryName() const { return "Terrain"; } diff --git a/components/terrain/terraingrid.cpp b/components/terrain/terraingrid.cpp index 74f683774..dbc1429da 100644 --- a/components/terrain/terraingrid.cpp +++ b/components/terrain/terraingrid.cpp @@ -34,7 +34,7 @@ TerrainGrid::~TerrainGrid() void TerrainGrid::cacheCell(View* view, int x, int y) { osg::Vec2f center(x+0.5f, y+0.5f); - static_cast(view)->mLoaded = buildTerrain(NULL, 1.f, center); + static_cast(view)->mLoaded = buildTerrain(nullptr, 1.f, center); } osg::ref_ptr TerrainGrid::buildTerrain (osg::Group* parent, float chunkSize, const osg::Vec2f& chunkCenter) @@ -57,7 +57,7 @@ osg::ref_ptr TerrainGrid::buildTerrain (osg::Group* parent, float chu { osg::ref_ptr node = mChunkManager->getChunk(chunkSize, chunkCenter, 0, 0); if (!node) - return NULL; + return nullptr; if (parent) parent->addChild(node); @@ -71,7 +71,7 @@ void TerrainGrid::loadCell(int x, int y) return; // already loaded osg::Vec2f center(x+0.5f, y+0.5f); - osg::ref_ptr terrainNode = buildTerrain(NULL, 1.f, center); + osg::ref_ptr terrainNode = buildTerrain(nullptr, 1.f, center); if (!terrainNode) return; // no terrain defined diff --git a/components/terrain/viewdata.cpp b/components/terrain/viewdata.cpp index 5c70f65f2..2f9cb6641 100644 --- a/components/terrain/viewdata.cpp +++ b/components/terrain/viewdata.cpp @@ -64,7 +64,7 @@ void ViewData::reset(unsigned int frame) { // clear any unused entries for (unsigned int i=mNumEntries; isecond; if (vd->getFrameLastUsed() + 2 < frame) { - vd->setViewer(NULL); + vd->setViewer(nullptr); vd->clear(); mUnusedViews.push_back(vd); mViews.erase(it++); diff --git a/components/terrain/world.hpp b/components/terrain/world.hpp index ae71693bd..da1004783 100644 --- a/components/terrain/world.hpp +++ b/components/terrain/world.hpp @@ -90,7 +90,7 @@ namespace Terrain /// Create a View to use with preload feature. The caller is responsible for deleting the view. /// @note Thread safe. - virtual View* createView() { return NULL; } + virtual View* createView() { return nullptr; } /// @note Thread safe, as long as you do not attempt to load into the same view from multiple threads. virtual void preload(View* view, const osg::Vec3f& eyePoint) {} diff --git a/components/translation/translation.cpp b/components/translation/translation.cpp index 14a32f178..ef0f43207 100644 --- a/components/translation/translation.cpp +++ b/components/translation/translation.cpp @@ -5,7 +5,7 @@ namespace Translation { Storage::Storage() - : mEncoder(NULL) + : mEncoder(nullptr) { } diff --git a/components/widgets/windowcaption.cpp b/components/widgets/windowcaption.cpp index 1c8fb5608..0fed4bead 100644 --- a/components/widgets/windowcaption.cpp +++ b/components/widgets/windowcaption.cpp @@ -6,9 +6,9 @@ namespace Gui { WindowCaption::WindowCaption() - : mLeft(NULL) - , mRight(NULL) - , mClient(NULL) + : mLeft(nullptr) + , mRight(nullptr) + , mClient(nullptr) { } From 5617bb3f0c0c70debcded2c00bdd12a642d5eb73 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 9 Oct 2018 11:29:57 +0400 Subject: [PATCH 115/163] Improve pitch factor handling for crossbow animations (bug #4672) --- CHANGELOG.md | 1 + apps/openmw/mwmechanics/character.cpp | 35 +++++++++------------------ 2 files changed, 12 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 226f09374..99cc5eabd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -134,6 +134,7 @@ Bug #4653: Length of non-ASCII strings is handled incorrectly in ESM reader Bug #4654: Editor: UpdateVisitor does not initialize skeletons for animated objects Bug #4668: Editor: Light source color is displayed as an integer + Bug #4672: Pitch factor is handled incorrectly for crossbow animations Feature #912: Editor: Add missing icons to UniversalId tables Feature #1221: Editor: Creature/NPC rendering Feature #1617: Editor: Enchantment effect record verifier diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index c4975b308..a74e7a49b 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1666,7 +1666,9 @@ bool CharacterController::updateWeaponState(CharacterState& idle) } mAnimation->setPitchFactor(0.f); - if (mWeaponType == WeapType_BowAndArrow || mWeaponType == WeapType_Thrown) + if (mWeaponType == WeapType_BowAndArrow || + mWeaponType == WeapType_Thrown || + mWeaponType == WeapType_Crossbow) { switch (mUpperBodyState) { @@ -1680,29 +1682,14 @@ bool CharacterController::updateWeaponState(CharacterState& idle) break; case UpperCharState_FollowStartToFollowStop: if (animPlaying) - mAnimation->setPitchFactor(1.f-complete); - break; - default: - break; - } - } - else if (mWeaponType == WeapType_Crossbow) - { - switch (mUpperBodyState) - { - case UpperCharState_EquipingWeap: - mAnimation->setPitchFactor(complete); - break; - case UpperCharState_UnEquipingWeap: - mAnimation->setPitchFactor(1.f-complete); - break; - case UpperCharState_WeapEquiped: - case UpperCharState_StartToMinAttack: - case UpperCharState_MinAttackToMaxAttack: - case UpperCharState_MaxAttackToMinHit: - case UpperCharState_MinHitToHit: - case UpperCharState_FollowStartToFollowStop: - mAnimation->setPitchFactor(1.f); + { + // technically we do not need a pitch for crossbow reload animation, + // but we should avoid abrupt repositioning + if (mWeaponType == WeapType_Crossbow) + mAnimation->setPitchFactor(std::max(0.f, 1.f-complete*10.f)); + else + mAnimation->setPitchFactor(1.f-complete); + } break; default: break; From 4ec727c50b4a624b9b191a0b74294fb15d995f84 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Wed, 10 Oct 2018 18:33:56 +0300 Subject: [PATCH 116/163] Disallow to open the journal while settings window is open (bug #4674) --- CHANGELOG.md | 1 + apps/openmw/mwinput/inputmanagerimp.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index aa877affb..3ad7e9aa6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -138,6 +138,7 @@ Bug #4669: ToggleCollision should trace the player down after collision being enabled Bug #4671: knownEffect functions should use modified Alchemy skill Bug #4672: Pitch factor is handled incorrectly for crossbow animations + Bug #4674: Journal can be opened when settings window is open Feature #912: Editor: Add missing icons to UniversalId tables Feature #1221: Editor: Creature/NPC rendering Feature #1617: Editor: Enchantment effect record verifier diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index b0a98bafc..0d017ba90 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -1107,6 +1107,7 @@ namespace MWInput if(MWBase::Environment::get().getWindowManager()->getMode() != MWGui::GM_Journal && MWBase::Environment::get().getWindowManager()->getMode() != MWGui::GM_MainMenu + && MWBase::Environment::get().getWindowManager()->getMode() != MWGui::GM_Settings && MWBase::Environment::get().getWindowManager ()->getJournalAllowed()) { MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Journal); From 47b1b0ac39c722de9409390b31e4371fcfb438a5 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Wed, 10 Oct 2018 22:44:26 +0300 Subject: [PATCH 117/163] Re-fix water reflections while making a no-GUI screenshot --- apps/openmw/mwgui/loadingscreen.cpp | 13 ++++---- apps/openmw/mwrender/renderingmanager.cpp | 37 +++++------------------ 2 files changed, 13 insertions(+), 37 deletions(-) diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp index 38ab70d54..3bb3ee260 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -169,19 +169,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); + mVisible = visible; + mLoadingBox->setVisible(mVisible); + setVisible(true); - if (!visible) + if (!mVisible) { + mShowWallpaper = false; draw(); return; } - mVisible = visible; - mLoadingBox->setVisible(mVisible); - - setVisible(true); + mShowWallpaper = MWBase::Environment::get().getStateManager()->getState() == MWBase::StateManager::State_NoGame; if (mShowWallpaper) { diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index bb0bb2f13..476beb990 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -805,7 +805,7 @@ namespace MWRender cubeTexture->setFilter(osg::Texture::MIN_FILTER,osg::Texture::NEAREST); cubeTexture->setFilter(osg::Texture::MAG_FILTER,osg::Texture::NEAREST); - + cubeTexture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); cubeTexture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE); @@ -830,19 +830,14 @@ namespace MWRender stateset->addUniform(new osg::Uniform("cubeMap",0)); stateset->addUniform(new osg::Uniform("mapping",screenshotMapping)); stateset->setTextureAttributeAndModes(0,cubeTexture,osg::StateAttribute::ON); - + quad->setStateSet(stateset); quad->setUpdateCallback(nullptr); screenshotCamera->addChild(quad); - mRootNode->addChild(screenshotCamera); - renderCameraToImage(screenshotCamera,image,screenshotW,screenshotH); - screenshotCamera->removeChildren(0,screenshotCamera->getNumChildren()); - mRootNode->removeChild(screenshotCamera); - return true; } @@ -867,6 +862,8 @@ namespace MWRender image->setDataType(GL_UNSIGNED_BYTE); image->setPixelFormat(texture->getInternalFormat()); + mRootNode->addChild(camera); + // The draw needs to complete before we can copy back our image. osg::ref_ptr callback (new NotifyDrawCompletedCallback); camera->setFinalDrawCallback(callback); @@ -882,32 +879,17 @@ namespace MWRender // now that we've "used up" the current frame, get a fresh framenumber for the next frame() following after the screenshot is completed mViewer->advance(mViewer->getFrameStamp()->getSimulationTime()); + + camera->removeChildren(0, camera->getNumChildren()); + mRootNode->removeChild(camera); } void RenderingManager::screenshot(osg::Image *image, int w, int h, osg::Matrixd cameraTransform) { osg::ref_ptr rttCamera (new osg::Camera); - rttCamera->setNodeMask(Mask_RenderToTexture); - rttCamera->attach(osg::Camera::COLOR_BUFFER, image); - rttCamera->setRenderOrder(osg::Camera::PRE_RENDER); - rttCamera->setReferenceFrame(osg::Camera::ABSOLUTE_RF); - rttCamera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT, osg::Camera::PIXEL_BUFFER_RTT); rttCamera->setProjectionMatrixAsPerspective(mFieldOfView, w/float(h), mNearClip, mViewDistance); rttCamera->setViewMatrix(mViewer->getCamera()->getViewMatrix() * cameraTransform); - rttCamera->setViewport(0, 0, w, h); - - osg::ref_ptr texture (new osg::Texture2D); - texture->setInternalFormat(GL_RGB); - texture->setTextureSize(w, h); - texture->setResizeNonPowerOfTwoHint(false); - texture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR); - texture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR); - rttCamera->attach(osg::Camera::COLOR_BUFFER, texture); - - image->setDataType(GL_UNSIGNED_BYTE); - image->setPixelFormat(texture->getInternalFormat()); - rttCamera->setUpdateCallback(new NoTraverseCallback); rttCamera->addChild(mSceneRoot); @@ -916,14 +898,9 @@ namespace MWRender rttCamera->setCullMask(mViewer->getCamera()->getCullMask() & (~Mask_GUI)); - mRootNode->addChild(rttCamera); - rttCamera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); renderCameraToImage(rttCamera.get(),image,w,h); - - rttCamera->removeChildren(0, rttCamera->getNumChildren()); - mRootNode->removeChild(rttCamera); } osg::Vec4f RenderingManager::getScreenBounds(const MWWorld::Ptr& ptr) From f9271fd7f090d75b2022373aa1028afaf49bac5c Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Fri, 12 Oct 2018 10:36:50 +0400 Subject: [PATCH 118/163] Fix crash in the ESM reader, when list is empty (bug #4677) --- CHANGELOG.md | 1 + components/esm/transport.cpp | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ad7e9aa6..2c45c8eb4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -139,6 +139,7 @@ Bug #4671: knownEffect functions should use modified Alchemy skill Bug #4672: Pitch factor is handled incorrectly for crossbow animations Bug #4674: Journal can be opened when settings window is open + Bug #4677: Crash in ESM reader when NPC record has DNAM record without DODT one Feature #912: Editor: Add missing icons to UniversalId tables Feature #1221: Editor: Creature/NPC rendering Feature #1617: Editor: Enchantment effect record verifier diff --git a/components/esm/transport.cpp b/components/esm/transport.cpp index 063c03329..11676ea72 100644 --- a/components/esm/transport.cpp +++ b/components/esm/transport.cpp @@ -1,5 +1,7 @@ #include "transport.hpp" +#include + #include #include @@ -16,7 +18,11 @@ namespace ESM } else if (esm.retSubName().intval == ESM::FourCC<'D','N','A','M'>::value) { - mList.back().mCellName = esm.getHString(); + const std::string name = esm.getHString(); + if (mList.empty()) + Log(Debug::Warning) << "Encountered DNAM record without DODT record, skipped."; + else + mList.back().mCellName = name; } } From ca8744af56812d7d51420b937b3a157304b10a3d Mon Sep 17 00:00:00 2001 From: jvoisin Date: Fri, 12 Oct 2018 09:28:18 +0200 Subject: [PATCH 119/163] Fix crash in the ESM reader, when SCVR has no variable names Fixes bug [#4678](https://gitlab.com/OpenMW/openmw/issues/4678) The `loadSCVR` method assumes that the list of variable names won't be empty, which it might. Instead of crashing, we show a warning and ignore the record. --- CHANGELOG.md | 1 + components/esm/loadscpt.cpp | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c45c8eb4..320e48693 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -140,6 +140,7 @@ Bug #4672: Pitch factor is handled incorrectly for crossbow animations Bug #4674: Journal can be opened when settings window is open Bug #4677: Crash in ESM reader when NPC record has DNAM record without DODT one + Bug #4678: Crash in ESP parser when SCVR has no variable names Feature #912: Editor: Add missing icons to UniversalId tables Feature #1221: Editor: Creature/NPC rendering Feature #1617: Editor: Enchantment effect record verifier diff --git a/components/esm/loadscpt.cpp b/components/esm/loadscpt.cpp index 474c2b423..808b416d7 100644 --- a/components/esm/loadscpt.cpp +++ b/components/esm/loadscpt.cpp @@ -30,6 +30,12 @@ namespace ESM // The tmp buffer is a null-byte separated string list, we // just have to pick out one string at a time. char* str = &tmp[0]; + if (!str && mVarNames.size() > 0) + { + Log(Debug::Warning) << "SCVR with no variable names"; + return; + } + for (size_t i = 0; i < mVarNames.size(); i++) { // Support '\r' terminated strings like vanilla. See Bug #1324. From dad0b78901b1ddb57949982c4099ea641749bfb2 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Fri, 12 Oct 2018 19:41:34 +0400 Subject: [PATCH 120/163] Avoid overflow when handling output characters (bug #4676) --- apps/openmw/mwgui/bookpage.cpp | 8 ++++---- apps/openmw/mwgui/bookpage.hpp | 5 ++++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwgui/bookpage.cpp b/apps/openmw/mwgui/bookpage.cpp index 29dfe7f3a..80e92f15a 100644 --- a/apps/openmw/mwgui/bookpage.cpp +++ b/apps/openmw/mwgui/bookpage.cpp @@ -506,7 +506,7 @@ struct TypesetBookImpl::Typesetter : BookTypesetter while (!stream.eof () && !ucsLineBreak (stream.peek ()) && ucsBreakingSpace (stream.peek ())) { MWGui::GlyphInfo info = GlyphInfo(style->mFont, stream.peek()); - if (info.codePoint >= 0) + if (info.charFound) space_width += static_cast(info.advance + info.bearingX); stream.consume (); } @@ -516,7 +516,7 @@ struct TypesetBookImpl::Typesetter : BookTypesetter while (!stream.eof () && !ucsLineBreak (stream.peek ()) && !ucsBreakingSpace (stream.peek ())) { MWGui::GlyphInfo info = GlyphInfo(style->mFont, stream.peek()); - if (info.codePoint >= 0) + if (info.charFound) word_width += static_cast(info.advance + info.bearingX); stream.consume (); } @@ -765,7 +765,7 @@ namespace { MWGui::GlyphInfo info = GlyphInfo(mFont, ch); - if (info.codePoint < 0) + if (!info.charFound) return; MyGUI::FloatRect vr; @@ -787,7 +787,7 @@ namespace { MWGui::GlyphInfo info = GlyphInfo(mFont, ch); - if (info.codePoint >= 0) + if (info.charFound) mCursor.left += static_cast(info.bearingX + info.advance); } diff --git a/apps/openmw/mwgui/bookpage.hpp b/apps/openmw/mwgui/bookpage.hpp index 4cf99794d..4ea59414d 100644 --- a/apps/openmw/mwgui/bookpage.hpp +++ b/apps/openmw/mwgui/bookpage.hpp @@ -43,6 +43,7 @@ namespace MWGui float advance; float bearingX; float bearingY; + bool charFound; MyGUI::FloatRect uvRect; GlyphInfo(MyGUI::IFont* font, MyGUI::Char ch) @@ -61,15 +62,17 @@ namespace MWGui height = (int) gi->height / scale; advance = (int) gi->advance / scale; uvRect = gi->uvRect; + charFound = true; } else { - codePoint = -1; + codePoint = 0; bearingX = 0; bearingY = 0; width = 0; height = 0; advance = 0; + charFound = false; } } }; From 292f4fcafe204be73543cfd862078bdc1e472815 Mon Sep 17 00:00:00 2001 From: rhtucker Date: Fri, 12 Oct 2018 21:09:27 -0700 Subject: [PATCH 121/163] Unified settings list and cfg file names. Added brief tutorial. --- docs/source/conf.py | 4 ++-- .../reference/modding/settings/index.rst | 18 +++++++++++++++++- .../reference/modding/settings/shaders.rst | 4 ++-- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index da59c02e1..60b25ae57 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -169,8 +169,8 @@ def setup(app): # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static', - 'manuals/openmw-cs/_static' +html_static_path = [ + '_static' ] # Add any extra paths that contain custom files (such as robots.txt or diff --git a/docs/source/reference/modding/settings/index.rst b/docs/source/reference/modding/settings/index.rst index 56d76a8d1..f7c86b567 100644 --- a/docs/source/reference/modding/settings/index.rst +++ b/docs/source/reference/modding/settings/index.rst @@ -8,10 +8,26 @@ If you are familiar with ``.ini`` tweaks in Morrowind or the other games, this w All settings described in this section are changed in ``settings.cfg``, located in your OpenMW user directory. See :doc:`../paths` for this location. +Changing Settings +################# + +#. Once you have located your ``settings.cfg`` file, open it in a plain text editor. +#. Find the setting(s) you wish to change in the following pages. +#. If the setting is not already in ``settings.cfg``, + add it by copy and pasting the name exactly as written in this guide. +#. Set the value of the setting by typing ``= `` after the setting on the same line, + using an appropriate value in place of ````. +#. If this is the first setting from it's category that you're adding, + be sure to add the heading in square brackets ``[]`` above it using just the setting type, + i.e. without the word "Settings". + + For example, to delay tooltips popping up by 1 second, add the line ``tooltip delay = 1.0``. + Then to the line above, type ``[GUI]``, as the tooltip delay setting comes from the "GUI Settings" section. + Although this guide attempts to be comprehensive and up to date, you will always be able to find the full list of settings available and their default values in ``settings-default.cfg`` in your main OpenMW installation directory. -The ranges I have included with each setting are the physically possible ranges, not recommendations. +The ranges included with each setting are the physically possible ranges, not recommendations. .. warning:: As the title suggests, these are advanced settings. diff --git a/docs/source/reference/modding/settings/shaders.rst b/docs/source/reference/modding/settings/shaders.rst index 17d092917..be1ecebf0 100644 --- a/docs/source/reference/modding/settings/shaders.rst +++ b/docs/source/reference/modding/settings/shaders.rst @@ -1,5 +1,5 @@ -Shader Settings -############### +Shaders Settings +################ force shaders ------------- From b5df385111241e14fcee20795b8c0a88431ffdbb Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Fri, 12 Oct 2018 14:11:41 +0400 Subject: [PATCH 122/163] Allow apps without logging system to display log messages --- components/debug/debuglog.hpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/components/debug/debuglog.hpp b/components/debug/debuglog.hpp index f4a8e17be..f5cdffeda 100644 --- a/components/debug/debuglog.hpp +++ b/components/debug/debuglog.hpp @@ -8,12 +8,13 @@ namespace Debug { enum Level { - NoLevel = 0, Error = 1, Warning = 2, Info = 3, Verbose = 4, - Marker = Verbose + Marker = Verbose, + + NoLevel = 5 // Do not filter messages in this case }; extern Level CurrentDebugLevel; @@ -30,6 +31,11 @@ public: mLock(sLock), mLevel(level) { + // If the app has no logging system enabled, log level is not specified. + // Show all messages without marker - we just use the plain cout in this case. + if (Debug::CurrentDebugLevel == Debug::NoLevel) + return; + if (mLevel <= Debug::CurrentDebugLevel) std::cout << static_cast(mLevel); } From ca07e3a36449e09ee7f8346161e7cb6beb6ffe3b Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sun, 30 Sep 2018 08:38:55 +0400 Subject: [PATCH 123/163] Check for obstacle before back up (bug #4656) --- CHANGELOG.md | 1 + apps/openmw/mwbase/world.hpp | 4 +- apps/openmw/mwmechanics/aicombat.cpp | 59 +++++++++++++++++++------ apps/openmw/mwmechanics/pathfinding.cpp | 5 ++- apps/openmw/mwworld/worldimp.cpp | 13 +++--- apps/openmw/mwworld/worldimp.hpp | 4 +- 6 files changed, 64 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 320e48693..abed0ba91 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -134,6 +134,7 @@ Bug #4649: Levelup fully restores health Bug #4653: Length of non-ASCII strings is handled incorrectly in ESM reader Bug #4654: Editor: UpdateVisitor does not initialize skeletons for animated objects + Bug #4656: Combat AI: back up behaviour is incorrect Bug #4668: Editor: Light source color is displayed as an integer Bug #4669: ToggleCollision should trace the player down after collision being enabled Bug #4671: knownEffect functions should use modified Alchemy skill diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index e17935abc..027d1fd10 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -297,9 +297,11 @@ namespace MWBase ///< Queues movement for \a ptr (in local space), to be applied in the next call to /// doPhysics. - virtual bool castRay (float x1, float y1, float z1, float x2, float y2, float z2, bool ignoreDoors=false) = 0; + virtual bool castRay (float x1, float y1, float z1, float x2, float y2, float z2, int mask) = 0; ///< cast a Ray and return true if there is an object in the ray path. + virtual bool castRay (float x1, float y1, float z1, float x2, float y2, float z2) = 0; + virtual bool toggleCollisionMode() = 0; ///< Toggle collision mode for player. If disabled player object should ignore /// collisions and gravity. diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 8f9545f99..a96832b69 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -4,6 +4,10 @@ #include +#include + +#include "../mwphysics/collisiontype.hpp" + #include "../mwworld/class.hpp" #include "../mwworld/esmstore.hpp" @@ -456,7 +460,48 @@ namespace MWMechanics mTimerCombatMove = 0.1f + 0.1f * Misc::Rng::rollClosedProbability(); mCombatMove = true; } + else if (isDistantCombat) + { + // Backing up behaviour + // Actor backs up slightly further away than opponent's weapon range + // (in vanilla - only as far as oponent's weapon range), + // or not at all if opponent is using a ranged weapon + + if (targetUsesRanged || distToTarget > rangeAttackOfTarget*1.5) // Don't back up if the target is wielding ranged weapon + return; + + // actor should not back up into water + if (MWBase::Environment::get().getWorld()->isUnderwater(MWWorld::ConstPtr(actor), 0.5f)) + return; + + int mask = MWPhysics::CollisionType_World | MWPhysics::CollisionType_HeightMap | MWPhysics::CollisionType_Door; + + // Actor can not back up if there is no free space behind + // Currently we take the 35% of actor's height from the ground as vector height. + // This approach allows us to detect small obstacles (e.g. crates) and curved walls. + osg::Vec3f halfExtents = MWBase::Environment::get().getWorld()->getHalfExtents(actor); + osg::Vec3f pos = actor.getRefData().getPosition().asVec3(); + osg::Vec3f source = pos + osg::Vec3f(0, 0, 0.75f * halfExtents.z()); + osg::Vec3f fallbackDirection = actor.getRefData().getBaseNode()->getAttitude() * osg::Vec3f(0,-1,0); + osg::Vec3f destination = source + fallbackDirection * (halfExtents.y() + 16); + + bool isObstacleDetected = MWBase::Environment::get().getWorld()->castRay(source.x(), source.y(), source.z(), destination.x(), destination.y(), destination.z(), mask); + if (isObstacleDetected) + return; + + // Check if there is nothing behind - probably actor is near cliff. + // A current approach: cast ray 1.5-yard ray down in 1.5 yard behind actor from 35% of actor's height. + // If we did not hit anything, there is a cliff behind actor. + source = pos + osg::Vec3f(0, 0, 0.75f * halfExtents.z()) + fallbackDirection * (halfExtents.y() + 96); + destination = source - osg::Vec3f(0, 0, 0.75f * halfExtents.z() + 96); + bool isCliffDetected = !MWBase::Environment::get().getWorld()->castRay(source.x(), source.y(), source.z(), destination.x(), destination.y(), destination.z(), mask); + if (isCliffDetected) + return; + + mMovement.mPosition[1] = -1; + } // dodge movements (for NPCs and bipedal creatures) + // Note: do not use for ranged combat yet since in couple with back up behaviour can move actor out of cliff else if (actor.getClass().isBipedal(actor)) { // apply sideway movement (kind of dodging) with some probability @@ -468,20 +513,6 @@ namespace MWMechanics mCombatMove = true; } } - - // Backing up behaviour - // Actor backs up slightly further away than opponent's weapon range - // (in vanilla - only as far as oponent's weapon range), - // or not at all if opponent is using a ranged weapon - if (isDistantCombat) - { - // actor should not back up into water - if (MWBase::Environment::get().getWorld()->isUnderwater(MWWorld::ConstPtr(actor), 0.5f)) - return; - - if (!targetUsesRanged && distToTarget <= rangeAttackOfTarget*1.5) // Don't back up if the target is wielding ranged weapon - mMovement.mPosition[1] = -1; - } } void AiCombatStorage::updateCombatMove(float duration) diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index 1da97a645..c16cff9e1 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -5,6 +5,8 @@ #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" +#include "../mwphysics/collisiontype.hpp" + #include "../mwworld/cellstore.hpp" #include "pathgrid.hpp" @@ -246,8 +248,9 @@ namespace MWMechanics converter.toWorld(temp); // Add Z offset since path node can overlap with other objects. // Also ignore doors in raytesting. + int mask = MWPhysics::CollisionType_World; bool isPathClear = !MWBase::Environment::get().getWorld()->castRay( - startPoint.mX, startPoint.mY, startPoint.mZ+16, temp.mX, temp.mY, temp.mZ+16, true); + startPoint.mX, startPoint.mY, startPoint.mZ+16, temp.mX, temp.mY, temp.mZ+16, mask); if (isPathClear) mPath.pop_front(); } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 0872e589d..0f20fa05a 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1505,15 +1505,18 @@ namespace MWWorld moveObjectImp(player->first, player->second.x(), player->second.y(), player->second.z(), false); } - bool World::castRay (float x1, float y1, float z1, float x2, float y2, float z2, bool ignoreDoors) + bool World::castRay (float x1, float y1, float z1, float x2, float y2, float z2) + { + int mask = MWPhysics::CollisionType_World | MWPhysics::CollisionType_Door; + bool result = castRay(x1, y1, z1, x2, y2, z2, mask); + return result; + } + + bool World::castRay (float x1, float y1, float z1, float x2, float y2, float z2, int mask) { osg::Vec3f a(x1,y1,z1); osg::Vec3f b(x2,y2,z2); - int mask = MWPhysics::CollisionType_World; - if (!ignoreDoors) - mask |= MWPhysics::CollisionType_Door; - MWPhysics::PhysicsSystem::RayResult result = mPhysics->castRay(a, b, MWWorld::Ptr(), std::vector(), mask); return result.mHit; } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 7df8d1af5..1592453a2 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -402,9 +402,11 @@ namespace MWWorld ///< Queues movement for \a ptr (in local space), to be applied in the next call to /// doPhysics. - bool castRay (float x1, float y1, float z1, float x2, float y2, float z2, bool ignoreDoors=false) override; + bool castRay (float x1, float y1, float z1, float x2, float y2, float z2, int mask) override; ///< cast a Ray and return true if there is an object in the ray path. + bool castRay (float x1, float y1, float z1, float x2, float y2, float z2) override; + bool toggleCollisionMode() override; ///< Toggle collision mode for player. If disabled player object should ignore /// collisions and gravity. From 19fd404b7ba0fd32e45473f91ae3c361758b7513 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 14 Oct 2018 14:44:32 +0300 Subject: [PATCH 124/163] Support soundgen calls for activators (feature #4285) --- CHANGELOG.md | 1 + apps/openmw/mwclass/activator.cpp | 70 +++++++++++++++++++++++++++++++ apps/openmw/mwclass/activator.hpp | 4 ++ 3 files changed, 75 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 320e48693..ddf20fa7f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -155,6 +155,7 @@ Feature #4012: Editor: Write a log file if OpenCS crashes Feature #4222: 360° screenshots Feature #4256: Implement ToggleBorders (TB) console command + Feature #4285: Support soundgen calls for activators Feature #4324: Add CFBundleIdentifier in Info.plist to allow for macOS function key shortcuts Feature #4345: Add equivalents for the command line commands to Launcher Feature #4404: Editor: All EnumDelegate fields should have their items sorted alphabetically diff --git a/apps/openmw/mwclass/activator.cpp b/apps/openmw/mwclass/activator.cpp index 8df262f24..7f53709d6 100644 --- a/apps/openmw/mwclass/activator.cpp +++ b/apps/openmw/mwclass/activator.cpp @@ -1,6 +1,7 @@ #include "activator.hpp" #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" @@ -134,4 +135,73 @@ namespace MWClass return MWWorld::Ptr(cell.insert(ref), &cell); } + + std::string Activator::getSoundIdFromSndGen(const MWWorld::Ptr &ptr, const std::string &name) const + { + std::string model = getModel(ptr); + if (model.empty()) + return std::string(); + + const MWWorld::Store &creaturestore = MWBase::Environment::get().getWorld()->getStore().get(); + std::string creatureId; + + for (const ESM::Creature &iter : creaturestore) + { + if (iter.mModel.empty()) + continue; + + if (Misc::StringUtils::ciEqual(model, "meshes\\" + iter.mModel)) + { + creatureId = !iter.mOriginal.empty() ? iter.mOriginal : iter.mId; + break; + } + } + + if (creatureId.empty()) + return std::string(); + + const MWWorld::Store &store = MWBase::Environment::get().getWorld()->getStore().get(); + + int type = getSndGenTypeFromName(name); + std::vector sounds; + + MWWorld::Store::iterator sound = store.begin(); + + while (sound != store.end()) + { + if (type == sound->mType && !sound->mCreature.empty() && (Misc::StringUtils::ciEqual(creatureId, sound->mCreature))) + sounds.push_back(&*sound); + ++sound; + } + + if (!sounds.empty()) + return sounds[Misc::Rng::rollDice(sounds.size())]->mSound; + + if (type == ESM::SoundGenerator::Land) + return "Body Fall Large"; + + return std::string(); + } + + int Activator::getSndGenTypeFromName(const std::string &name) const + { + if (name == "left") + return 0; + if (name == "right") + return 1; + if (name == "swimleft") + return 2; + if (name == "swimright") + return 3; + if (name == "moan") + return 4; + if (name == "roar") + return 5; + if (name == "scream") + return 6; + if (name == "land") + return 7; + + throw std::runtime_error(std::string("Unexpected soundgen type: ")+name); + } } diff --git a/apps/openmw/mwclass/activator.hpp b/apps/openmw/mwclass/activator.hpp index 3f333f4cb..0f66b74b4 100644 --- a/apps/openmw/mwclass/activator.hpp +++ b/apps/openmw/mwclass/activator.hpp @@ -44,6 +44,10 @@ namespace MWClass ///< Whether or not to use animated variant of model (default false) virtual bool isActivator() const; + + virtual std::string getSoundIdFromSndGen(const MWWorld::Ptr &ptr, const std::string &name) const; + + virtual int getSndGenTypeFromName(const std::string &name) const; }; } From 6ef7be3fd3e50925b26bd8d0cd2cb3b87e1846db Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 14 Oct 2018 20:11:21 +0300 Subject: [PATCH 125/163] Re-enable using soundgen land for creatures --- apps/openmw/mwclass/npc.cpp | 10 ++-------- apps/openmw/mwmechanics/character.cpp | 22 ++++++++++++---------- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 4339f37e2..d6dafd2a2 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -1242,15 +1242,9 @@ namespace MWClass return ""; } + // Morrowind ignores land soundgen for NPCs if(name == "land") - { - MWBase::World *world = MWBase::Environment::get().getWorld(); - osg::Vec3f pos(ptr.getRefData().getPosition().asVec3()); - if (world->isUnderwater(ptr.getCell(), pos) || world->isWalkingOnWater(ptr)) - return "DefaultLandWater"; - - return "DefaultLand"; - } + return ""; if(name == "swimleft") return "Swim Left"; if(name == "swimright") diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 7ad8d61cd..0931aeda1 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -979,17 +979,13 @@ void CharacterController::handleTextKey(const std::string &groupname, const std: } } - if (soundgen == "land") // Morrowind ignores land soundgen for some reason - return; - std::string sound = mPtr.getClass().getSoundIdFromSndGen(mPtr, soundgen); if(!sound.empty()) { MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); - if(soundgen == "left" || soundgen == "right") + // NB: landing sound is not played for NPCs here + if(soundgen == "left" || soundgen == "right" || soundgen == "land") { - // Don't make foot sounds local for the player, it makes sense to keep them - // positioned on the ground. sndMgr->playSound3D(mPtr, sound, volume, pitch, MWSound::Type::Foot, MWSound::PlayMode::NoPlayerLocal); } @@ -2071,11 +2067,17 @@ void CharacterController::update(float duration) } } - // Play landing sound - MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); - std::string sound = cls.getSoundIdFromSndGen(mPtr, "land"); - if (!sound.empty()) + // Play landing sound for NPCs + if (mPtr.getClass().isNpc()) + { + MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); + std::string sound = "DefaultLand"; + osg::Vec3f pos(mPtr.getRefData().getPosition().asVec3()); + if (world->isUnderwater(mPtr.getCell(), pos) || world->isWalkingOnWater(mPtr)) + sound = "DefaultLandWater"; + sndMgr->playSound3D(mPtr, sound, 1.f, 1.f, MWSound::Type::Foot, MWSound::PlayMode::NoPlayerLocal); + } } else { From bf3f82b9d4c6f259fe50fe12063b6838d688283a Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 14 Oct 2018 20:37:40 +0300 Subject: [PATCH 126/163] Cleanup --- apps/openmw/mwclass/activator.cpp | 43 +++++++++++-------------------- apps/openmw/mwclass/activator.hpp | 4 +-- apps/openmw/mwclass/creature.cpp | 20 +++++++------- 3 files changed, 27 insertions(+), 40 deletions(-) diff --git a/apps/openmw/mwclass/activator.cpp b/apps/openmw/mwclass/activator.cpp index 7f53709d6..e0e201391 100644 --- a/apps/openmw/mwclass/activator.cpp +++ b/apps/openmw/mwclass/activator.cpp @@ -138,19 +138,13 @@ namespace MWClass std::string Activator::getSoundIdFromSndGen(const MWWorld::Ptr &ptr, const std::string &name) const { - std::string model = getModel(ptr); - if (model.empty()) - return std::string(); - - const MWWorld::Store &creaturestore = MWBase::Environment::get().getWorld()->getStore().get(); + std::string model = getModel(ptr); // Assume it's not empty, since we wouldn't have gotten the soundgen otherwise + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); std::string creatureId; - - for (const ESM::Creature &iter : creaturestore) - { - if (iter.mModel.empty()) - continue; - if (Misc::StringUtils::ciEqual(model, "meshes\\" + iter.mModel)) + for (const ESM::Creature &iter : store.get()) + { + if (!iter.mModel.empty() && Misc::StringUtils::ciEqual(model, "meshes\\" + iter.mModel)) { creatureId = !iter.mOriginal.empty() ? iter.mOriginal : iter.mId; break; @@ -160,19 +154,12 @@ namespace MWClass if (creatureId.empty()) return std::string(); - const MWWorld::Store &store = MWBase::Environment::get().getWorld()->getStore().get(); - int type = getSndGenTypeFromName(name); std::vector sounds; - MWWorld::Store::iterator sound = store.begin(); - - while (sound != store.end()) - { + for (auto sound = store.get().begin(); sound != store.get().end(); ++sound) if (type == sound->mType && !sound->mCreature.empty() && (Misc::StringUtils::ciEqual(creatureId, sound->mCreature))) sounds.push_back(&*sound); - ++sound; - } if (!sounds.empty()) return sounds[Misc::Rng::rollDice(sounds.size())]->mSound; @@ -183,24 +170,24 @@ namespace MWClass return std::string(); } - int Activator::getSndGenTypeFromName(const std::string &name) const + int Activator::getSndGenTypeFromName(const std::string &name) { if (name == "left") - return 0; + return ESM::SoundGenerator::LeftFoot; if (name == "right") - return 1; + return ESM::SoundGenerator::RightFoot; if (name == "swimleft") - return 2; + return ESM::SoundGenerator::SwimLeft; if (name == "swimright") - return 3; + return ESM::SoundGenerator::SwimRight; if (name == "moan") - return 4; + return ESM::SoundGenerator::Moan; if (name == "roar") - return 5; + return ESM::SoundGenerator::Roar; if (name == "scream") - return 6; + return ESM::SoundGenerator::Scream; if (name == "land") - return 7; + return ESM::SoundGenerator::Land; throw std::runtime_error(std::string("Unexpected soundgen type: ")+name); } diff --git a/apps/openmw/mwclass/activator.hpp b/apps/openmw/mwclass/activator.hpp index 0f66b74b4..b92dc75cb 100644 --- a/apps/openmw/mwclass/activator.hpp +++ b/apps/openmw/mwclass/activator.hpp @@ -10,6 +10,8 @@ namespace MWClass virtual MWWorld::Ptr copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const; + static int getSndGenTypeFromName(const std::string &name); + public: virtual void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const; @@ -46,8 +48,6 @@ namespace MWClass virtual bool isActivator() const; virtual std::string getSoundIdFromSndGen(const MWWorld::Ptr &ptr, const std::string &name) const; - - virtual int getSndGenTypeFromName(const std::string &name) const; }; } diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 6d0b42bfe..788e5cd68 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -688,9 +688,9 @@ namespace MWClass MWBase::World *world = MWBase::Environment::get().getWorld(); osg::Vec3f pos(ptr.getRefData().getPosition().asVec3()); if(world->isUnderwater(ptr.getCell(), pos) || world->isWalkingOnWater(ptr)) - return 2; + return ESM::SoundGenerator::SwimLeft; if(world->isOnGround(ptr)) - return 0; + return ESM::SoundGenerator::LeftFoot; return -1; } if(name == "right") @@ -698,23 +698,23 @@ namespace MWClass MWBase::World *world = MWBase::Environment::get().getWorld(); osg::Vec3f pos(ptr.getRefData().getPosition().asVec3()); if(world->isUnderwater(ptr.getCell(), pos) || world->isWalkingOnWater(ptr)) - return 3; + return ESM::SoundGenerator::SwimRight; if(world->isOnGround(ptr)) - return 1; + return ESM::SoundGenerator::RightFoot; return -1; } if(name == "swimleft") - return 2; + return ESM::SoundGenerator::SwimLeft; if(name == "swimright") - return 3; + return ESM::SoundGenerator::SwimRight; if(name == "moan") - return 4; + return ESM::SoundGenerator::Moan; if(name == "roar") - return 5; + return ESM::SoundGenerator::Roar; if(name == "scream") - return 6; + return ESM::SoundGenerator::Scream; if(name == "land") - return 7; + return ESM::SoundGenerator::Land; throw std::runtime_error(std::string("Unexpected soundgen type: ")+name); } From 13bd81f8969808d6bd13fc4d24e375b67c2ab504 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 16 Oct 2018 22:28:19 +0400 Subject: [PATCH 127/163] Try to use collisions from basic actor model if an animated one has no collisions (feature #4682) --- CHANGELOG.md | 1 + apps/openmw/mwphysics/physicssystem.cpp | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ddf20fa7f..cda9150b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -175,6 +175,7 @@ Feature #4632: AI priority: utilize vanilla AI GMSTs for priority rating Feature #4636: Use sTo GMST in spellmaking menu Feature #4642: Batching potion creation + Feature #4682: Use the collision box from basic creature mesh if the X one have no collisions Task #2490: Don't open command prompt window on Release-mode builds automatically Task #4545: Enable is_pod string test Task #4605: Optimize skinning diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 363f28e70..2ce498f37 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -1337,6 +1337,16 @@ namespace MWPhysics if (!shape) return; + // Try to get shape from basic model as fallback for creatures + if (!ptr.getClass().isNpc() && shape->mCollisionBoxHalfExtents.length2() == 0) + { + const std::string fallbackModel = ptr.getClass().getModel(ptr); + if (fallbackModel != mesh) + { + shape = mShapeManager->getShape(fallbackModel); + } + } + Actor* actor = new Actor(ptr, shape, mCollisionWorld); mActors.insert(std::make_pair(ptr, actor)); } From d7d9050d4ae558708a4a291a3e0709d14cee452a Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Thu, 18 Oct 2018 11:42:03 +0400 Subject: [PATCH 128/163] Force actor to the 'weapon equipped' state if the weapon disappeared in the middle of attack (bug #4646) --- CHANGELOG.md | 1 + apps/openmw/mwmechanics/character.cpp | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a4d5d34f..44932f2ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -130,6 +130,7 @@ Bug #4633: Sneaking stance affects speed even if the actor is not able to crouch Bug #4641: GetPCJumping is handled incorrectly Bug #4644: %Name should be available for all actors, not just for NPCs + Bug #4646: Weapon force-equipment messes up ongoing attack animations Bug #4648: Hud thinks that throwing weapons have condition Bug #4649: Levelup fully restores health Bug #4653: Length of non-ASCII strings is handled incorrectly in ESM reader diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 0931aeda1..ce844674f 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1278,6 +1278,18 @@ bool CharacterController::updateWeaponState(CharacterState& idle) bool isStillWeapon = weaptype > WeapType_HandToHand && weaptype < WeapType_Spell && mWeaponType > WeapType_HandToHand && mWeaponType < WeapType_Spell; + // If the current weapon type was changed in the middle of attack (e.g. by Equip console command or when bound spell expires), + // we should force actor to the "weapon equipped" state, interrupt attack and update animations. + if (isStillWeapon && mWeaponType != weaptype && mUpperBodyState > UpperCharState_WeapEquiped) + { + forcestateupdate = true; + mUpperBodyState = UpperCharState_WeapEquiped; + mAttackingOrSpell = false; + mAnimation->disable(mCurrentWeapon); + if (mPtr == getPlayer()) + MWBase::Environment::get().getWorld()->getPlayer().setAttackingOrSpell(false); + } + if(!isKnockedOut() && !isKnockedDown() && !isRecovery()) { std::string weapgroup; From 8fa0ffcfe4e0753651c1435fc1aed0dc735b6cce Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Thu, 18 Oct 2018 14:59:39 +0400 Subject: [PATCH 129/163] Catch exceptions inside the loadVoice() (bug #4685) --- CHANGELOG.md | 1 + apps/openmw/mwsound/soundmanagerimp.cpp | 36 +++++++++++++++++-------- apps/openmw/mwsound/soundmanagerimp.hpp | 2 +- 3 files changed, 27 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a4d5d34f..14abe86b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -142,6 +142,7 @@ Bug #4674: Journal can be opened when settings window is open Bug #4677: Crash in ESM reader when NPC record has DNAM record without DODT one Bug #4678: Crash in ESP parser when SCVR has no variable names + Bug #4685: Missing sound causes an exception inside Say command Feature #912: Editor: Add missing icons to UniversalId tables Feature #1221: Editor: Creature/NPC rendering Feature #1617: Editor: Enchantment effect record verifier diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 8e3488906..7b722a835 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -245,20 +245,30 @@ namespace MWSound DecoderPtr SoundManager::loadVoice(const std::string &voicefile) { - DecoderPtr decoder = getDecoder(); - // Workaround: Bethesda at some point converted some of the files to mp3, but the references were kept as .wav. - if(mVFS->exists(voicefile)) - decoder->open(voicefile); - else + try { - std::string file = voicefile; - std::string::size_type pos = file.rfind('.'); - if(pos != std::string::npos) - file = file.substr(0, pos)+".mp3"; - decoder->open(file); + DecoderPtr decoder = getDecoder(); + + // Workaround: Bethesda at some point converted some of the files to mp3, but the references were kept as .wav. + if(mVFS->exists(voicefile)) + decoder->open(voicefile); + else + { + std::string file = voicefile; + std::string::size_type pos = file.rfind('.'); + if(pos != std::string::npos) + file = file.substr(0, pos)+".mp3"; + decoder->open(file); + } + + return decoder; + } + catch(std::exception &e) + { + Log(Debug::Error) << "Failed to load audio from " << voicefile << ": " << e.what(); } - return decoder; + return nullptr; } Sound *SoundManager::getSoundRef() @@ -471,6 +481,8 @@ namespace MWSound mVFS->normalizeFilename(voicefile); DecoderPtr decoder = loadVoice(voicefile); + if (!decoder) + return; MWBase::World *world = MWBase::Environment::get().getWorld(); const osg::Vec3f pos = world->getActorHeadTransform(ptr).getTrans(); @@ -503,6 +515,8 @@ namespace MWSound mVFS->normalizeFilename(voicefile); DecoderPtr decoder = loadVoice(voicefile); + if (!decoder) + return; stopSay(MWWorld::ConstPtr()); Stream *sound = playVoice(decoder, osg::Vec3f(), true); diff --git a/apps/openmw/mwsound/soundmanagerimp.hpp b/apps/openmw/mwsound/soundmanagerimp.hpp index 4064a05af..d8a4cfc8c 100644 --- a/apps/openmw/mwsound/soundmanagerimp.hpp +++ b/apps/openmw/mwsound/soundmanagerimp.hpp @@ -117,7 +117,7 @@ namespace MWSound Sound_Buffer *lookupSound(const std::string &soundId) const; Sound_Buffer *loadSound(const std::string &soundId); - // returns a decoder to start streaming + // returns a decoder to start streaming, or nullptr if the sound was not found DecoderPtr loadVoice(const std::string &voicefile); Sound *getSoundRef(); From 46bf45a6e2f0a30aa8f34c2a6c64406c36a5def7 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Fri, 19 Oct 2018 13:09:39 +0400 Subject: [PATCH 130/163] Remove redundant code --- apps/openmw/mwrender/animation.cpp | 9 --------- 1 file changed, 9 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 6e2c76d1d..f165b6bd3 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -190,12 +190,6 @@ namespace { } - RemoveFinishedCallbackVisitor(int effectId) - : RemoveVisitor() - , mHasMagicEffects(false) - { - } - virtual void apply(osg::Node &node) { traverse(node); @@ -228,9 +222,6 @@ namespace virtual void apply(osg::Geometry&) { } - - private: - int mEffectId; }; class RemoveCallbackVisitor : public RemoveVisitor From 41e90bd56c7e57f632d116f0f5cf10ec6c9906d8 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Fri, 19 Oct 2018 14:37:25 +0400 Subject: [PATCH 131/163] Unify random generator usage --- apps/opencs/editor.cpp | 3 +++ components/nifosg/particle.cpp | 12 +++++++----- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index 450b434e6..6699ccaaf 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #include "model/doc/document.hpp" @@ -355,6 +356,8 @@ int CS::Editor::run() if (mLocal.empty()) return 1; + Misc::Rng::init(); + mStartup.show(); QApplication::setQuitOnLastWindowClosed (true); diff --git a/components/nifosg/particle.cpp b/components/nifosg/particle.cpp index f5c055a15..c1f6a2819 100644 --- a/components/nifosg/particle.cpp +++ b/components/nifosg/particle.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -81,17 +82,17 @@ ParticleShooter::ParticleShooter(const ParticleShooter ©, const osg::CopyOp void ParticleShooter::shoot(osgParticle::Particle *particle) const { - float hdir = mHorizontalDir + mHorizontalAngle * (2.f * (std::rand() / static_cast(RAND_MAX)) - 1.f); - float vdir = mVerticalDir + mVerticalAngle * (2.f * (std::rand() / static_cast(RAND_MAX)) - 1.f); + float hdir = mHorizontalDir + mHorizontalAngle * (2.f * Misc::Rng::rollClosedProbability() - 1.f); + float vdir = mVerticalDir + mVerticalAngle * (2.f * Misc::Rng::rollClosedProbability() - 1.f); osg::Vec3f dir = (osg::Quat(vdir, osg::Vec3f(0,1,0)) * osg::Quat(hdir, osg::Vec3f(0,0,1))) * osg::Vec3f(0,0,1); - float vel = mMinSpeed + (mMaxSpeed - mMinSpeed) * std::rand() / static_cast(RAND_MAX); + float vel = mMinSpeed + (mMaxSpeed - mMinSpeed) * Misc::Rng::rollClosedProbability(); particle->setVelocity(dir * vel); // Not supposed to set this here, but there doesn't seem to be a better way of doing it - particle->setLifeTime(mLifetime + mLifetimeRandom * std::rand() / static_cast(RAND_MAX)); + particle->setLifeTime(mLifetime + mLifetimeRandom * Misc::Rng::rollClosedProbability()); } GrowFadeAffector::GrowFadeAffector(float growTime, float fadeTime) @@ -277,7 +278,8 @@ void Emitter::emitParticles(double dt) if (!mTargets.empty()) { - int randomRecIndex = mTargets[(std::rand() / (static_cast(RAND_MAX)+1.0)) * mTargets.size()]; + int randomIndex = Misc::Rng::rollClosedProbability() * (mTargets.size() - 1); + int randomRecIndex = mTargets[randomIndex]; // we could use a map here for faster lookup FindGroupByRecIndex visitor(randomRecIndex); From 229d1bb4258b48c274ca0315503f9b0e0812dbdc Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Fri, 19 Oct 2018 19:43:19 +0400 Subject: [PATCH 132/163] Backport loop from tinyxml 2.6 to avoid CVE --- extern/oics/tinyxml.cpp | 59 +++++++++++++---------------------------- 1 file changed, 18 insertions(+), 41 deletions(-) diff --git a/extern/oics/tinyxml.cpp b/extern/oics/tinyxml.cpp index f1cdc8192..b61df85c8 100644 --- a/extern/oics/tinyxml.cpp +++ b/extern/oics/tinyxml.cpp @@ -1046,58 +1046,35 @@ bool TiXmlDocument::LoadFile( FILE* file, TiXmlEncoding encoding ) return false; } - const char* lastPos = buf; - const char* p = buf; + const char* p = buf; // the read head + char* q = buf; // the write head + const char CR = 0x0d; + const char LF = 0x0a; buf[length] = 0; while( *p ) { assert( p < (buf+length) ); - if ( *p == 0xa ) { - // Newline character. No special rules for this. Append all the characters - // since the last string, and include the newline. - data.append( lastPos, (p-lastPos+1) ); // append, include the newline - ++p; // move past the newline - lastPos = p; // and point to the new buffer (may be 0) - assert( p <= (buf+length) ); - } - else if ( *p == 0xd ) { - // Carriage return. Append what we have so far, then - // handle moving forward in the buffer. - if ( (p-lastPos) > 0 ) { - data.append( lastPos, p-lastPos ); // do not add the CR - } - data += (char)0xa; // a proper newline + assert( q <= (buf+length) ); + assert( q <= p ); - if ( *(p+1) == 0xa ) { - // Carriage return - new line sequence - p += 2; - lastPos = p; - assert( p <= (buf+length) ); - } - else { - // it was followed by something else...that is presumably characters again. - ++p; - lastPos = p; - assert( p <= (buf+length) ); + if ( *p == CR ) { + *q++ = LF; + p++; + if ( *p == LF ) { // check for CR+LF (and skip LF) + p++; } } else { - ++p; + *q++ = *p++; } } - // Handle any left over characters. - if ( p-lastPos ) { - data.append( lastPos, p-lastPos ); - } + assert( q <= (buf+length) ); + *q = 0; + + Parse( buf, 0, encoding ); + delete [] buf; - buf = 0; - - Parse( data.c_str(), 0, encoding ); - - if ( Error() ) - return false; - else - return true; + return !Error(); } From 9809eef18e8587969a787f7e057157592fb8e963 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 20 Oct 2018 22:14:15 +0300 Subject: [PATCH 133/163] Utilize the default soundgen entries when necessary (bug #4689) --- CHANGELOG.md | 1 + apps/openmw/mwclass/activator.cpp | 38 +++++++++++++++++++++---------- apps/openmw/mwclass/creature.cpp | 12 ++++++---- 3 files changed, 34 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b707851de..150c294fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -144,6 +144,7 @@ Bug #4677: Crash in ESM reader when NPC record has DNAM record without DODT one Bug #4678: Crash in ESP parser when SCVR has no variable names Bug #4685: Missing sound causes an exception inside Say command + Bug #4689: Default creature soundgen entries are not used Feature #912: Editor: Add missing icons to UniversalId tables Feature #1221: Editor: Creature/NPC rendering Feature #1617: Editor: Enchantment effect record verifier diff --git a/apps/openmw/mwclass/activator.cpp b/apps/openmw/mwclass/activator.cpp index e0e201391..42ea99b00 100644 --- a/apps/openmw/mwclass/activator.cpp +++ b/apps/openmw/mwclass/activator.cpp @@ -138,7 +138,7 @@ namespace MWClass std::string Activator::getSoundIdFromSndGen(const MWWorld::Ptr &ptr, const std::string &name) const { - std::string model = getModel(ptr); // Assume it's not empty, since we wouldn't have gotten the soundgen otherwise + const std::string model = getModel(ptr); // Assume it's not empty, since we wouldn't have gotten the soundgen otherwise const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); std::string creatureId; @@ -151,21 +151,35 @@ namespace MWClass } } - if (creatureId.empty()) - return std::string(); - int type = getSndGenTypeFromName(name); - std::vector sounds; - for (auto sound = store.get().begin(); sound != store.get().end(); ++sound) - if (type == sound->mType && !sound->mCreature.empty() && (Misc::StringUtils::ciEqual(creatureId, sound->mCreature))) - sounds.push_back(&*sound); + std::vector fallbacksounds; + if (!creatureId.empty()) + { + std::vector sounds; + for (auto sound = store.get().begin(); sound != store.get().end(); ++sound) + { + if (type == sound->mType && !sound->mCreature.empty() && (Misc::StringUtils::ciEqual(creatureId, sound->mCreature))) + sounds.push_back(&*sound); + if (type == sound->mType && sound->mCreature.empty()) + fallbacksounds.push_back(&*sound); + } - if (!sounds.empty()) - return sounds[Misc::Rng::rollDice(sounds.size())]->mSound; + if (!sounds.empty()) + return sounds[Misc::Rng::rollDice(sounds.size())]->mSound; + if (!fallbacksounds.empty()) + return fallbacksounds[Misc::Rng::rollDice(fallbacksounds.size())]->mSound; + } + else + { + // The activator doesn't have a corresponding creature ID, but we can try to use the defaults + for (auto sound = store.get().begin(); sound != store.get().end(); ++sound) + if (type == sound->mType && sound->mCreature.empty()) + fallbacksounds.push_back(&*sound); - if (type == ESM::SoundGenerator::Land) - return "Body Fall Large"; + if (!fallbacksounds.empty()) + return fallbacksounds[Misc::Rng::rollDice(fallbacksounds.size())]->mSound; + } return std::string(); } diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 788e5cd68..e03635b0c 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -632,25 +632,27 @@ namespace MWClass if(type >= 0) { std::vector sounds; + std::vector fallbacksounds; MWWorld::LiveCellRef* ref = ptr.get(); const std::string& ourId = (ref->mBase->mOriginal.empty()) ? ptr.getCellRef().getRefId() : ref->mBase->mOriginal; MWWorld::Store::iterator sound = store.begin(); - while(sound != store.end()) + while (sound != store.end()) { if (type == sound->mType && !sound->mCreature.empty() && (Misc::StringUtils::ciEqual(ourId, sound->mCreature))) sounds.push_back(&*sound); + if (type == sound->mType && sound->mCreature.empty()) + fallbacksounds.push_back(&*sound); ++sound; } - if(!sounds.empty()) + if (!sounds.empty()) return sounds[Misc::Rng::rollDice(sounds.size())]->mSound; + if (!fallbacksounds.empty()) + return fallbacksounds[Misc::Rng::rollDice(fallbacksounds.size())]->mSound; } - if (type == ESM::SoundGenerator::Land) - return "Body Fall Large"; - return ""; } From 61e6e359c4595fd28f48dbb1a106931737c8d8c3 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Mon, 22 Oct 2018 15:14:25 +0400 Subject: [PATCH 134/163] Allow creatures to use the autogenerated collision box (feature #2787) --- CHANGELOG.md | 1 + apps/openmw/mwphysics/actor.cpp | 26 ++++++++++++++++++++++++++ components/resource/bulletshape.hpp | 3 +-- 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b707851de..a4e736467 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -149,6 +149,7 @@ Feature #1617: Editor: Enchantment effect record verifier Feature #1645: Casting effects from objects Feature #2606: Editor: Implemented (optional) case sensitive global search + Feature #2787: Use the autogenerated collision box, if the creature mesh has no predefined one Feature #2847: Content selector: allow to copy the path to a file by using the context menu 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/mwphysics/actor.cpp b/apps/openmw/mwphysics/actor.cpp index 79c6dcabf..b55c20455 100644 --- a/apps/openmw/mwphysics/actor.cpp +++ b/apps/openmw/mwphysics/actor.cpp @@ -6,6 +6,7 @@ #include #include +#include #include "../mwworld/class.hpp" @@ -28,6 +29,31 @@ Actor::Actor(const MWWorld::Ptr& ptr, osg::ref_ptr mHalfExtents = shape->mCollisionBoxHalfExtents; mMeshTranslation = shape->mCollisionBoxTranslate; + // We can not create actor without collisions - he will fall through the ground. + // In this case we should autogenerate collision box based on mesh shape + // (NPCs have bodyparts and use a different approach) + if (!ptr.getClass().isNpc() && mHalfExtents.length2() == 0.f) + { + const Resource::BulletShape* collisionShape = shape.get(); + if (collisionShape && collisionShape->mCollisionShape) + { + btTransform transform; + transform.setIdentity(); + btVector3 min; + btVector3 max; + + collisionShape->mCollisionShape->getAabb(transform, min, max); + mHalfExtents.x() = (max[0] - min[0])/2.f; + mHalfExtents.y() = (max[1] - min[1])/2.f; + mHalfExtents.z() = (max[2] - min[2])/2.f; + + mMeshTranslation = osg::Vec3f(0.f, 0.f, mHalfExtents.z()); + } + + if (mHalfExtents.length2() == 0.f) + Log(Debug::Error) << "Error: Failed to calculate bounding box for actor \"" << ptr.getCellRef().getRefId() << "\"."; + } + // Use capsule shape only if base is square (nonuniform scaling apparently doesn't work on it) if (std::abs(mHalfExtents.x()-mHalfExtents.y())= mHalfExtents.x()) { diff --git a/components/resource/bulletshape.hpp b/components/resource/bulletshape.hpp index a418bb28c..b30b5045c 100644 --- a/components/resource/bulletshape.hpp +++ b/components/resource/bulletshape.hpp @@ -26,8 +26,7 @@ namespace Resource btCollisionShape* mCollisionShape; - // Used for actors. Note, ideally actors would use a separate loader - as it is - // we have to keep a redundant copy of the actor model around in mCollisionShape, which isn't used. + // Used for actors. mCollisionShape is used for actors only when we need to autogenerate collision box for creatures. // For now, use one file <-> one resource for simplicity. osg::Vec3f mCollisionBoxHalfExtents; osg::Vec3f mCollisionBoxTranslate; From 4ce35c6ad558cd25d9ddf5e1d1f94b8e84c476f0 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Wed, 24 Oct 2018 01:40:57 +0300 Subject: [PATCH 135/163] Fix fixme behavior in interiors --- apps/openmw/mwbase/world.hpp | 4 ++-- apps/openmw/mwscript/transformationextensions.cpp | 3 +-- apps/openmw/mwworld/worldimp.cpp | 5 +++-- apps/openmw/mwworld/worldimp.hpp | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 027d1fd10..8da3e6112 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -262,8 +262,8 @@ namespace MWBase ///< Adjust position after load to be on ground. Must be called after model load. /// @param force do this even if the ptr is flying - virtual void fixPosition (const MWWorld::Ptr& actor) = 0; - ///< Attempt to fix position so that the Ptr is no longer inside collision geometry. + virtual void fixPosition () = 0; + ///< Attempt to fix position so that the player is not stuck inside the geometry. /// @note No-op for items in containers. Use ContainerStore::removeItem instead. virtual void deleteObject (const MWWorld::Ptr& ptr) = 0; diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index 2695aed76..9f0784d6c 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -738,8 +738,7 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { - const MWWorld::Ptr ptr = MWMechanics::getPlayer(); - MWBase::Environment::get().getWorld()->fixPosition(ptr); + MWBase::Environment::get().getWorld()->fixPosition(); } }; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 0f20fa05a..84dbe921e 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1351,8 +1351,9 @@ namespace MWWorld moveObject(ptr, ptr.getCell(), pos.x(), pos.y(), pos.z()); } - void World::fixPosition(const Ptr &actor) + void World::fixPosition() { + const MWWorld::Ptr actor = getPlayerPtr(); const float distance = 128.f; ESM::Position esmPos = actor.getRefData().getPosition(); osg::Quat orientation(esmPos.rot[2], osg::Vec3f(0,0,-1)); @@ -1382,7 +1383,7 @@ namespace MWWorld esmPos.pos[0] = traced.x(); esmPos.pos[1] = traced.y(); esmPos.pos[2] = traced.z(); - MWWorld::ActionTeleport("", esmPos, false).execute(actor); + MWWorld::ActionTeleport(actor.getCell()->isExterior() ? "" : actor.getCell()->getCell()->mName, esmPos, false).execute(actor); } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 1592453a2..3a42a7ee0 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -291,8 +291,8 @@ namespace MWWorld ///< Adjust position after load to be on ground. Must be called after model load. /// @param force do this even if the ptr is flying - void fixPosition (const Ptr& actor) override; - ///< Attempt to fix position so that the Ptr is no longer inside collision geometry. + void fixPosition () override; + ///< Attempt to fix position so that the player is not stuck inside the geometry. void enable (const Ptr& ptr) override; From 67de61e1fb3bf14ef0b5f6bb1cecc3b6b4d3d363 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Wed, 24 Oct 2018 18:51:34 +0300 Subject: [PATCH 136/163] Avoid item condition and charge zero divisions --- apps/openmw/mwclass/armor.cpp | 2 +- apps/openmw/mwclass/npc.cpp | 11 +++++- apps/openmw/mwclass/weapon.cpp | 2 +- apps/openmw/mwgui/merchantrepair.cpp | 2 +- apps/openmw/mwgui/sortfilteritemmodel.cpp | 48 +++++++++++++++++++++-- apps/openmw/mwgui/tradewindow.cpp | 11 +++++- apps/openmw/mwgui/windowmanagerimp.cpp | 29 ++++++++++++-- apps/openmw/mwmechanics/combat.cpp | 11 +++++- 8 files changed, 100 insertions(+), 16 deletions(-) diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index b90c1ec58..ad64ad0d1 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -297,7 +297,7 @@ namespace MWClass { const MWWorld::InventoryStore& invStore = npc.getClass().getInventoryStore(npc); - if (ptr.getCellRef().getCharge() == 0) + if (getItemHealth(ptr) == 0) return std::make_pair(0, "#{sInventoryMessage1}"); // slots that this item can be equipped in diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index d6dafd2a2..f7172ac0b 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -1148,9 +1148,16 @@ namespace MWClass const bool hasHealth = it->getClass().hasItemHealth(*it); if (hasHealth) { - int armorHealth = it->getClass().getItemHealth(*it); int armorMaxHealth = it->getClass().getItemMaxHealth(*it); - ratings[i] *= (float(armorHealth) / armorMaxHealth); + if (armorMaxHealth == 0) + { + ratings[i] = 0; + } + else + { + int armorHealth = it->getClass().getItemHealth(*it); + ratings[i] *= (float(armorHealth) / armorMaxHealth); + } } } } diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index 78678f461..7d28c8983 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -383,7 +383,7 @@ namespace MWClass std::pair Weapon::canBeEquipped(const MWWorld::ConstPtr &ptr, const MWWorld::Ptr &npc) const { - if (hasItemHealth(ptr) && ptr.getCellRef().getCharge() == 0) + if (hasItemHealth(ptr) && getItemHealth(ptr) == 0) return std::make_pair(0, "#{sInventoryMessage1}"); // Do not allow equip weapons from inventory during attack diff --git a/apps/openmw/mwgui/merchantrepair.cpp b/apps/openmw/mwgui/merchantrepair.cpp index b9d4c80f4..282c0e4ea 100644 --- a/apps/openmw/mwgui/merchantrepair.cpp +++ b/apps/openmw/mwgui/merchantrepair.cpp @@ -51,7 +51,7 @@ void MerchantRepair::setPtr(const MWWorld::Ptr &actor) { int maxDurability = iter->getClass().getItemMaxHealth(*iter); int durability = iter->getClass().getItemHealth(*iter); - if (maxDurability == durability) + if (maxDurability == durability || maxDurability == 0) continue; int basePrice = iter->getClass().getValue(*iter); diff --git a/apps/openmw/mwgui/sortfilteritemmodel.cpp b/apps/openmw/mwgui/sortfilteritemmodel.cpp index fe7f61952..5172c85ee 100644 --- a/apps/openmw/mwgui/sortfilteritemmodel.cpp +++ b/apps/openmw/mwgui/sortfilteritemmodel.cpp @@ -86,26 +86,66 @@ namespace if (!leftName.empty()) { const ESM::Enchantment* ench = MWBase::Environment::get().getWorld()->getStore().get().search(leftName); + if (ench) { if (ench->mData.mType == ESM::Enchantment::ConstantEffect) + { leftChargePercent = 101; + } else - leftChargePercent = (left.mBase.getCellRef().getEnchantmentCharge() == -1) ? 100 - : static_cast(left.mBase.getCellRef().getEnchantmentCharge() / static_cast(ench->mData.mCharge) * 100); + { + int maxEnchCharge = ench->mData.mCharge; + if (maxEnchCharge == 0) + { + leftChargePercent = 0; + } + else + { + float enchCharge = left.mBase.getCellRef().getEnchantmentCharge(); + if (enchCharge == -1) + { + leftChargePercent = 100; + } + else + { + leftChargePercent = static_cast(enchCharge / static_cast(maxEnchCharge) * 100); + } + } + } } } if (!rightName.empty()) { const ESM::Enchantment* ench = MWBase::Environment::get().getWorld()->getStore().get().search(rightName); + if (ench) { if (ench->mData.mType == ESM::Enchantment::ConstantEffect) + { rightChargePercent = 101; + } else - rightChargePercent = (right.mBase.getCellRef().getEnchantmentCharge() == -1) ? 100 - : static_cast(right.mBase.getCellRef().getEnchantmentCharge() / static_cast(ench->mData.mCharge) * 100); + { + int maxEnchCharge = ench->mData.mCharge; + if (maxEnchCharge == 0) + { + rightChargePercent = 0; + } + else + { + float enchCharge = right.mBase.getCellRef().getEnchantmentCharge(); + if (enchCharge == -1) + { + rightChargePercent = 100; + } + else + { + rightChargePercent = static_cast(enchCharge / static_cast(maxEnchCharge) * 100); + } + } + } } } diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index 4404b2b1a..2fd91fd4a 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -35,8 +35,15 @@ namespace float price = static_cast(item.getClass().getValue(item)); if (item.getClass().hasItemHealth(item)) { - price *= item.getClass().getItemHealth(item); - price /= item.getClass().getItemMaxHealth(item); + if (item.getClass().getItemMaxHealth(item) == 0) + { + price = 0; + } + else + { + price *= item.getClass().getItemHealth(item); + price /= item.getClass().getItemMaxHealth(item); + } } return static_cast(price * count); } diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index e4515fdc3..3b26edecb 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -1369,8 +1369,22 @@ namespace MWGui const ESM::Enchantment* ench = mStore->get() .find(item.getClass().getEnchantment(item)); - int chargePercent = (item.getCellRef().getEnchantmentCharge() == -1) ? 100 - : static_cast(item.getCellRef().getEnchantmentCharge() / static_cast(ench->mData.mCharge) * 100); + int chargePercent = 100; + + int maxEnchCharge = ench->mData.mCharge; + if (maxEnchCharge == 0) + { + chargePercent = 0; + } + else + { + float enchCharge = item.getCellRef().getEnchantmentCharge(); + if (enchCharge != -1) + { + chargePercent = static_cast(enchCharge / static_cast(maxEnchCharge) * 100); + } + } + mHud->setSelectedEnchantItem(item, chargePercent); mSpellWindow->setTitle(item.getClass().getName(item)); } @@ -1386,7 +1400,16 @@ namespace MWGui int durabilityPercent = 100; if (item.getClass().hasItemHealth(item)) { - durabilityPercent = static_cast(item.getClass().getItemHealth(item) / static_cast(item.getClass().getItemMaxHealth(item)) * 100); + int weapmaxhealth = item.getClass().getItemMaxHealth(item); + if (weapmaxhealth == 0) + { + durabilityPercent = 0; + } + else + { + int weaphealth = item.getClass().getItemHealth(item); + durabilityPercent = static_cast(weaphealth / static_cast(weapmaxhealth) * 100); + } } mHud->setSelectedWeapon(item, durabilityPercent); mInventoryWindow->setTitle(item.getClass().getName(item)); diff --git a/apps/openmw/mwmechanics/combat.cpp b/apps/openmw/mwmechanics/combat.cpp index be55b681f..2a9ff5e84 100644 --- a/apps/openmw/mwmechanics/combat.cpp +++ b/apps/openmw/mwmechanics/combat.cpp @@ -371,10 +371,17 @@ namespace MWMechanics return; const bool weaphashealth = weapon.getClass().hasItemHealth(weapon); - if(weaphashealth) + if (weaphashealth) { - int weaphealth = weapon.getClass().getItemHealth(weapon); int weapmaxhealth = weapon.getClass().getItemMaxHealth(weapon); + + if (weapmaxhealth == 0) + { + damage = 0; + return; + } + + int weaphealth = weapon.getClass().getItemHealth(weapon); damage *= (float(weaphealth) / weapmaxhealth); } From 54bd7b2dcff287451c2eea0b318b098ad5e90a35 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Thu, 25 Oct 2018 15:45:31 +0300 Subject: [PATCH 137/163] Implement getItemNormalizedHealth() method and use it --- apps/openmw/mwclass/npc.cpp | 11 +---------- apps/openmw/mwgui/tradewindow.cpp | 13 ++----------- apps/openmw/mwgui/windowmanagerimp.cpp | 12 ++---------- apps/openmw/mwmechanics/combat.cpp | 11 +---------- apps/openmw/mwworld/class.cpp | 12 ++++++++++++ apps/openmw/mwworld/class.hpp | 3 +++ 6 files changed, 21 insertions(+), 41 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index f7172ac0b..b8b1e9600 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -1148,16 +1148,7 @@ namespace MWClass const bool hasHealth = it->getClass().hasItemHealth(*it); if (hasHealth) { - int armorMaxHealth = it->getClass().getItemMaxHealth(*it); - if (armorMaxHealth == 0) - { - ratings[i] = 0; - } - else - { - int armorHealth = it->getClass().getItemHealth(*it); - ratings[i] *= (float(armorHealth) / armorMaxHealth); - } + ratings[i] *= it->getClass().getItemNormalizedHealth(*it); } } } diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index 2fd91fd4a..ce8193da2 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -34,17 +34,8 @@ namespace { float price = static_cast(item.getClass().getValue(item)); if (item.getClass().hasItemHealth(item)) - { - if (item.getClass().getItemMaxHealth(item) == 0) - { - price = 0; - } - else - { - price *= item.getClass().getItemHealth(item); - price /= item.getClass().getItemMaxHealth(item); - } - } + price *= item.getClass().getItemNormalizedHealth(item); + return static_cast(price * count); } diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 3b26edecb..27ea534a7 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -1400,17 +1400,9 @@ namespace MWGui int durabilityPercent = 100; if (item.getClass().hasItemHealth(item)) { - int weapmaxhealth = item.getClass().getItemMaxHealth(item); - if (weapmaxhealth == 0) - { - durabilityPercent = 0; - } - else - { - int weaphealth = item.getClass().getItemHealth(item); - durabilityPercent = static_cast(weaphealth / static_cast(weapmaxhealth) * 100); - } + durabilityPercent = static_cast(item.getClass().getItemNormalizedHealth(item)); } + mHud->setSelectedWeapon(item, durabilityPercent); mInventoryWindow->setTitle(item.getClass().getName(item)); } diff --git a/apps/openmw/mwmechanics/combat.cpp b/apps/openmw/mwmechanics/combat.cpp index 2a9ff5e84..41e2485ce 100644 --- a/apps/openmw/mwmechanics/combat.cpp +++ b/apps/openmw/mwmechanics/combat.cpp @@ -373,16 +373,7 @@ namespace MWMechanics const bool weaphashealth = weapon.getClass().hasItemHealth(weapon); if (weaphashealth) { - int weapmaxhealth = weapon.getClass().getItemMaxHealth(weapon); - - if (weapmaxhealth == 0) - { - damage = 0; - return; - } - - int weaphealth = weapon.getClass().getItemHealth(weapon); - damage *= (float(weaphealth) / weapmaxhealth); + damage *= weapon.getClass().getItemNormalizedHealth(weapon); } static const float fDamageStrengthBase = MWBase::Environment::get().getWorld()->getStore().get() diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index 68d5998ac..70fde33cf 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -83,6 +83,18 @@ namespace MWWorld return ptr.getCellRef().getCharge(); } + float Class::getItemNormalizedHealth (const ConstPtr& ptr) const + { + if (getItemMaxHealth(ptr) == 0) + { + return 0.f; + } + else + { + return getItemHealth(ptr) / static_cast(getItemMaxHealth(ptr)); + } + } + int Class::getItemMaxHealth (const ConstPtr& ptr) const { throw std::runtime_error ("class does not have item health"); diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index 132b7e9e8..af88b0dcc 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -112,6 +112,9 @@ namespace MWWorld virtual int getItemHealth (const ConstPtr& ptr) const; ///< Return current item health or throw an exception if class does not have item health + virtual float getItemNormalizedHealth (const ConstPtr& ptr) const; + ///< Return current item health re-scaled to maximum health + virtual int getItemMaxHealth (const ConstPtr& ptr) const; ///< Return item max health or throw an exception, if class does not have item health /// (default implementation: throw an exception) From c3e8d536cdbfedd8dccdfac24b9fd10299376f1b Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Thu, 25 Oct 2018 16:09:07 +0300 Subject: [PATCH 138/163] Implement getNormalizedEnchantmentCharge() method and use it --- apps/openmw/mwgui/sortfilteritemmodel.cpp | 46 +---------------------- apps/openmw/mwgui/tradewindow.cpp | 3 +- apps/openmw/mwgui/windowmanagerimp.cpp | 18 +-------- apps/openmw/mwworld/cellref.cpp | 16 ++++++++ apps/openmw/mwworld/cellref.hpp | 3 ++ 5 files changed, 24 insertions(+), 62 deletions(-) diff --git a/apps/openmw/mwgui/sortfilteritemmodel.cpp b/apps/openmw/mwgui/sortfilteritemmodel.cpp index 5172c85ee..23f8a121b 100644 --- a/apps/openmw/mwgui/sortfilteritemmodel.cpp +++ b/apps/openmw/mwgui/sortfilteritemmodel.cpp @@ -86,66 +86,24 @@ namespace if (!leftName.empty()) { const ESM::Enchantment* ench = MWBase::Environment::get().getWorld()->getStore().get().search(leftName); - if (ench) { if (ench->mData.mType == ESM::Enchantment::ConstantEffect) - { leftChargePercent = 101; - } else - { - int maxEnchCharge = ench->mData.mCharge; - if (maxEnchCharge == 0) - { - leftChargePercent = 0; - } - else - { - float enchCharge = left.mBase.getCellRef().getEnchantmentCharge(); - if (enchCharge == -1) - { - leftChargePercent = 100; - } - else - { - leftChargePercent = static_cast(enchCharge / static_cast(maxEnchCharge) * 100); - } - } - } + leftChargePercent = static_cast(left.mBase.getCellRef().getNormalizedEnchantmentCharge(ench->mData.mCharge) * 100); } } if (!rightName.empty()) { const ESM::Enchantment* ench = MWBase::Environment::get().getWorld()->getStore().get().search(rightName); - if (ench) { if (ench->mData.mType == ESM::Enchantment::ConstantEffect) - { rightChargePercent = 101; - } else - { - int maxEnchCharge = ench->mData.mCharge; - if (maxEnchCharge == 0) - { - rightChargePercent = 0; - } - else - { - float enchCharge = right.mBase.getCellRef().getEnchantmentCharge(); - if (enchCharge == -1) - { - rightChargePercent = 100; - } - else - { - rightChargePercent = static_cast(enchCharge / static_cast(maxEnchCharge) * 100); - } - } - } + rightChargePercent = static_cast(right.mBase.getCellRef().getNormalizedEnchantmentCharge(ench->mData.mCharge) * 100); } } diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index ce8193da2..90698dfc4 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -34,8 +34,9 @@ namespace { float price = static_cast(item.getClass().getValue(item)); if (item.getClass().hasItemHealth(item)) + { price *= item.getClass().getItemNormalizedHealth(item); - + } return static_cast(price * count); } diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 27ea534a7..5ffed4815 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -1369,22 +1369,7 @@ namespace MWGui const ESM::Enchantment* ench = mStore->get() .find(item.getClass().getEnchantment(item)); - int chargePercent = 100; - - int maxEnchCharge = ench->mData.mCharge; - if (maxEnchCharge == 0) - { - chargePercent = 0; - } - else - { - float enchCharge = item.getCellRef().getEnchantmentCharge(); - if (enchCharge != -1) - { - chargePercent = static_cast(enchCharge / static_cast(maxEnchCharge) * 100); - } - } - + int chargePercent = static_cast(item.getCellRef().getNormalizedEnchantmentCharge(ench->mData.mCharge) * 100); mHud->setSelectedEnchantItem(item, chargePercent); mSpellWindow->setTitle(item.getClass().getName(item)); } @@ -1402,7 +1387,6 @@ namespace MWGui { durabilityPercent = static_cast(item.getClass().getItemNormalizedHealth(item)); } - mHud->setSelectedWeapon(item, durabilityPercent); mInventoryWindow->setTitle(item.getClass().getName(item)); } diff --git a/apps/openmw/mwworld/cellref.cpp b/apps/openmw/mwworld/cellref.cpp index 72ee56e6a..094669bdd 100644 --- a/apps/openmw/mwworld/cellref.cpp +++ b/apps/openmw/mwworld/cellref.cpp @@ -70,6 +70,22 @@ namespace MWWorld return mCellRef.mEnchantmentCharge; } + float CellRef::getNormalizedEnchantmentCharge(int maxCharge) const + { + if (maxCharge == 0) + { + return 0; + } + else if (mCellRef.mEnchantmentCharge == -1) + { + return 1; + } + else + { + return mCellRef.mEnchantmentCharge / static_cast(maxCharge); + } + } + void CellRef::setEnchantmentCharge(float charge) { if (charge != mCellRef.mEnchantmentCharge) diff --git a/apps/openmw/mwworld/cellref.hpp b/apps/openmw/mwworld/cellref.hpp index 7e27e6ef3..5646bafb0 100644 --- a/apps/openmw/mwworld/cellref.hpp +++ b/apps/openmw/mwworld/cellref.hpp @@ -56,6 +56,9 @@ namespace MWWorld // Remaining enchantment charge. This could be -1 if the charge was not touched yet (i.e. full). float getEnchantmentCharge() const; + // Remaining enchantment charge rescaled to the supplied maximum charge (such as one of the enchantment). + float getNormalizedEnchantmentCharge(int maxCharge) const; + void setEnchantmentCharge(float charge); // For weapon or armor, this is the remaining item health. From e7de6b974aac469adfa9bb23956ac961104133bf Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Fri, 21 Sep 2018 16:34:23 +0400 Subject: [PATCH 139/163] Optimize actors processing 1. Do not update physics and animations for actors outside processing range (bug #4647) 2. Do not render such actors 3. Add transparency to actors near processing border, so they will not pop up suddenly --- apps/openmw/mwbase/mechanicsmanager.hpp | 4 + apps/openmw/mwbase/world.hpp | 3 + apps/openmw/mwgui/settingswindow.cpp | 2 + apps/openmw/mwmechanics/actors.cpp | 97 ++++++++++++------- apps/openmw/mwmechanics/actors.hpp | 4 + apps/openmw/mwmechanics/aipackage.cpp | 4 +- apps/openmw/mwmechanics/aitravel.cpp | 6 +- apps/openmw/mwmechanics/character.cpp | 52 ++++++---- apps/openmw/mwmechanics/character.hpp | 3 +- .../mwmechanics/mechanicsmanagerimp.cpp | 19 ++++ .../mwmechanics/mechanicsmanagerimp.hpp | 6 ++ apps/openmw/mwphysics/physicssystem.cpp | 27 ++++++ apps/openmw/mwphysics/physicssystem.hpp | 2 + apps/openmw/mwrender/animation.cpp | 32 +++--- apps/openmw/mwworld/worldimp.cpp | 16 +++ apps/openmw/mwworld/worldimp.hpp | 3 + .../reference/modding/settings/game.rst | 15 ++- files/mygui/openmw_settings_window.layout | 33 ++++++- files/settings-default.cfg | 3 + 19 files changed, 258 insertions(+), 73 deletions(-) diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index 8137bad95..056628211 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -233,6 +233,10 @@ namespace MWBase virtual void castSpell(const MWWorld::Ptr& ptr, const std::string spellId, bool manualSpell) = 0; + virtual void processChangedSettings (const std::set< std::pair >& settings) = 0; + + virtual float getActorsProcessingRange() const = 0; + /// Check if the target actor was detected by an observer /// If the observer is a non-NPC, check all actors in AI processing distance as observers virtual bool isActorDetected(const MWWorld::Ptr& actor, const MWWorld::Ptr& observer) = 0; diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 027d1fd10..864b81158 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -302,6 +302,9 @@ namespace MWBase virtual bool castRay (float x1, float y1, float z1, float x2, float y2, float z2) = 0; + virtual void setActorCollisionMode(const MWWorld::Ptr& ptr, bool enabled) = 0; + virtual bool isActorCollisionEnabled(const MWWorld::Ptr& ptr) = 0; + virtual bool toggleCollisionMode() = 0; ///< Toggle collision mode for player. If disabled player object should ignore /// collisions and gravity. diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 80ed9202a..6e6924f28 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -20,6 +20,7 @@ #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/inputmanager.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "confirmationdialog.hpp" @@ -437,6 +438,7 @@ namespace MWGui MWBase::Environment::get().getSoundManager()->processChangedSettings(changed); MWBase::Environment::get().getWindowManager()->processChangedSettings(changed); MWBase::Environment::get().getInputManager()->processChangedSettings(changed); + MWBase::Environment::get().getMechanicsManager()->processChangedSettings(changed); } void SettingsWindow::onKeyboardSwitchClicked(MyGUI::Widget* _sender) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 2a68591a8..d4350e07c 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -147,9 +147,6 @@ void getRestorationPerHourOfSleep (const MWWorld::Ptr& ptr, float& health, float namespace MWMechanics { - const float aiProcessingDistance = 7168; - const float sqrAiProcessingDistance = aiProcessingDistance*aiProcessingDistance; - class SoulTrap : public MWMechanics::EffectSourceVisitor { MWWorld::Ptr mCreature; @@ -364,7 +361,8 @@ namespace MWMechanics const ESM::Position& actor1Pos = actor1.getRefData().getPosition(); const ESM::Position& actor2Pos = actor2.getRefData().getPosition(); float sqrDist = (actor1Pos.asVec3() - actor2Pos.asVec3()).length2(); - if (sqrDist > sqrAiProcessingDistance) + + if (sqrDist > mActorsProcessingRange*mActorsProcessingRange) return; // No combat for totally static creatures @@ -1130,8 +1128,11 @@ namespace MWMechanics } } - Actors::Actors() { + Actors::Actors() + { mTimerDisposeSummonsCorpses = 0.2f; // We should add a delay between summoned creature death and its corpse despawning + + updateProcessingRange(); } Actors::~Actors() @@ -1139,6 +1140,23 @@ namespace MWMechanics clear(); } + float Actors::getProcessingRange() const + { + return mActorsProcessingRange; + } + + void Actors::updateProcessingRange() + { + // We have to cap it since using high values (larger than 7168) will make some quests harder or impossible to complete (bug #1876) + static const float maxProcessingRange = 7168.f; + static const float minProcessingRange = maxProcessingRange / 2.f; + + float actorsProcessingRange = Settings::Manager::getFloat("actors processing range", "Game"); + actorsProcessingRange = std::min(actorsProcessingRange, maxProcessingRange); + actorsProcessingRange = std::max(actorsProcessingRange, minProcessingRange); + mActorsProcessingRange = actorsProcessingRange; + } + void Actors::addActor (const MWWorld::Ptr& ptr, bool updateImmediately) { removeActor(ptr); @@ -1184,7 +1202,7 @@ namespace MWMechanics // Otherwise check if any actor in AI processing range sees the target actor std::vector actors; osg::Vec3f position (actor.getRefData().getPosition().asVec3()); - getObjectsInRange(position, aiProcessingDistance, actors); + getObjectsInRange(position, mActorsProcessingRange, actors); for(std::vector::iterator it = actors.begin(); it != actors.end(); ++it) { if (*it == actor) @@ -1242,7 +1260,7 @@ namespace MWMechanics { if (iter->first == player) continue; - bool inProcessingRange = (playerPos - iter->first.getRefData().getPosition().asVec3()).length2() <= sqrAiProcessingDistance; + bool inProcessingRange = (playerPos - iter->first.getRefData().getPosition().asVec3()).length2() <= mActorsProcessingRange*mActorsProcessingRange; if (inProcessingRange) { MWMechanics::CreatureStats& stats = iter->first.getClass().getCreatureStats(iter->first); @@ -1288,7 +1306,8 @@ namespace MWMechanics if (timerUpdateEquippedLight >= updateEquippedLightInterval) timerUpdateEquippedLight = 0; // show torches only when there are darkness and no precipitations - bool showTorches = MWBase::Environment::get().getWorld()->useTorches(); + MWBase::World* world = MWBase::Environment::get().getWorld(); + bool showTorches = world->useTorches(); MWWorld::Ptr player = getPlayer(); const osg::Vec3f playerPos = player.getRefData().getPosition().asVec3(); @@ -1301,7 +1320,7 @@ namespace MWMechanics int attackedByPlayerId = player.getClass().getCreatureStats(player).getHitAttemptActorId(); if (attackedByPlayerId != -1) { - const MWWorld::Ptr playerHitAttemptActor = MWBase::Environment::get().getWorld()->searchPtrViaActorId(attackedByPlayerId); + const MWWorld::Ptr playerHitAttemptActor = world->searchPtrViaActorId(attackedByPlayerId); if (!playerHitAttemptActor.isInCell()) player.getClass().getCreatureStats(player).setHitAttemptActorId(-1); @@ -1314,14 +1333,11 @@ namespace MWMechanics CharacterController* ctrl = iter->second->getCharacterController(); float distSqr = (playerPos - 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) - // This distance could be made configurable later, but the setting must be marked with a big warning: - // using higher values will make a quest in Bloodmoon harder or impossible to complete (bug #1876) - bool inProcessingRange = distSqr <= sqrAiProcessingDistance; + // AI processing is only done within given distance to the player. + bool inProcessingRange = distSqr <= mActorsProcessingRange*mActorsProcessingRange; if (isPlayer) - ctrl->setAttackingOrSpell(MWBase::Environment::get().getWorld()->getPlayer().getAttackingOrSpell()); + ctrl->setAttackingOrSpell(world->getPlayer().getAttackingOrSpell()); // If dead or no longer in combat, no longer store any actors who attempted to hit us. Also remove for the player. if (iter->first != player && (iter->first.getClass().getCreatureStats(iter->first).isDead() @@ -1335,10 +1351,10 @@ namespace MWMechanics if (!iter->first.getClass().getCreatureStats(iter->first).isDead()) { - bool cellChanged = MWBase::Environment::get().getWorld()->hasCellChanged(); + bool cellChanged = world->hasCellChanged(); MWWorld::Ptr actor = iter->first; // make a copy of the map key to avoid it being invalidated when the player teleports updateActor(actor, duration); - if (!cellChanged && MWBase::Environment::get().getWorld()->hasCellChanged()) + if (!cellChanged && world->hasCellChanged()) { return; // for now abort update of the old cell when cell changes by teleportation magic effect // a better solution might be to apply cell changes at the end of the frame @@ -1363,7 +1379,7 @@ namespace MWMechanics MWWorld::Ptr headTrackTarget; MWMechanics::CreatureStats& stats = iter->first.getClass().getCreatureStats(iter->first); - bool firstPersonPlayer = isPlayer && MWBase::Environment::get().getWorld()->isFirstPerson(); + bool firstPersonPlayer = isPlayer && world->isFirstPerson(); // 1. Unconsious actor can not track target // 2. Actors in combat and pursue mode do not bother to headtrack @@ -1423,27 +1439,25 @@ namespace MWMechanics CharacterController* playerCharacter = nullptr; for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) { - const float animationDistance = aiProcessingDistance + 400; // Slightly larger than AI distance so there is time to switch back to the idle animation. - const float distSqr = (playerPos - iter->first.getRefData().getPosition().asVec3()).length2(); + const float dist = (playerPos - iter->first.getRefData().getPosition().asVec3()).length(); bool isPlayer = iter->first == player; - bool inAnimationRange = isPlayer || (animationDistance == 0 || distSqr <= animationDistance*animationDistance); + bool inRange = isPlayer || dist <= mActorsProcessingRange; int activeFlag = 1; // Can be changed back to '2' to keep updating bounding boxes off screen (more accurate, but slower) if (isPlayer) activeFlag = 2; - int active = inAnimationRange ? activeFlag : 0; - bool canFly = iter->first.getClass().canFly(iter->first); - if (canFly) - { - // Keep animating flying creatures so they don't just hover in-air - inAnimationRange = true; - active = std::max(1, active); - } + int active = inRange ? activeFlag : 0; CharacterController* ctrl = iter->second->getCharacterController(); ctrl->setActive(active); - if (!inAnimationRange) + if (!inRange) + { + iter->first.getRefData().getBaseNode()->setNodeMask(0); + world->setActorCollisionMode(iter->first, false); continue; + } + else if (!isPlayer) + iter->first.getRefData().getBaseNode()->setNodeMask(1<<3); if (iter->first.getClass().getCreatureStats(iter->first).isParalyzed()) ctrl->skipAnim(); @@ -1455,11 +1469,28 @@ namespace MWMechanics playerCharacter = ctrl; continue; } + + world->setActorCollisionMode(iter->first, true); ctrl->update(duration); + + // Fade away actors on large distance (>90% of actor's processing distance) + float visibilityRatio = 1.0; + float fadeStartDistance = mActorsProcessingRange*0.9f; + float fadeEndDistance = mActorsProcessingRange; + float fadeRatio = (dist - fadeStartDistance)/(fadeEndDistance - fadeStartDistance); + if (fadeRatio > 0) + visibilityRatio -= std::max(0.f, fadeRatio); + + visibilityRatio = std::min(1.f, visibilityRatio); + + ctrl->setVisibility(visibilityRatio); } if (playerCharacter) + { playerCharacter->update(duration); + playerCharacter->setVisibility(1.f); + } for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) { @@ -1671,7 +1702,7 @@ namespace MWMechanics restoreDynamicStats(iter->first, sleep); if ((!iter->first.getRefData().getBaseNode()) || - (playerPos - iter->first.getRefData().getPosition().asVec3()).length2() > sqrAiProcessingDistance) + (playerPos - iter->first.getRefData().getPosition().asVec3()).length2() > mActorsProcessingRange*mActorsProcessingRange) continue; adjustMagicEffects (iter->first); @@ -1915,7 +1946,7 @@ namespace MWMechanics std::list list; std::vector neighbors; osg::Vec3f position (actor.getRefData().getPosition().asVec3()); - getObjectsInRange(position, aiProcessingDistance, neighbors); + getObjectsInRange(position, mActorsProcessingRange, neighbors); for(auto neighbor = neighbors.begin(); neighbor != neighbors.end(); ++neighbor) { if (*neighbor == actor) @@ -1936,7 +1967,7 @@ namespace MWMechanics std::list list; std::vector neighbors; osg::Vec3f position (actor.getRefData().getPosition().asVec3()); - getObjectsInRange(position, aiProcessingDistance, neighbors); + getObjectsInRange(position, mActorsProcessingRange, neighbors); std::set followers; getActorsFollowing(actor, followers); diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index a1e0e511d..61879c432 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -65,6 +65,9 @@ namespace MWMechanics /// paused we may want to do it manually (after equipping permanent enchantment) void updateMagicEffects (const MWWorld::Ptr& ptr); + void updateProcessingRange(); + float getProcessingRange() const; + void addActor (const MWWorld::Ptr& ptr, bool updateImmediately=false); ///< Register an actor for stats management /// @@ -168,6 +171,7 @@ namespace MWMechanics private: PtrActorMap mActors; float mTimerDisposeSummonsCorpses; + float mActorsProcessingRange; }; } diff --git a/apps/openmw/mwmechanics/aipackage.cpp b/apps/openmw/mwmechanics/aipackage.cpp index 9a42f191e..7045d28e5 100644 --- a/apps/openmw/mwmechanics/aipackage.cpp +++ b/apps/openmw/mwmechanics/aipackage.cpp @@ -103,7 +103,7 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, const ESM::Pathgr ESM::Position pos = actor.getRefData().getPosition(); //position of the actor /// Stops the actor when it gets too close to a unloaded cell - //... At current time, this test is unnecessary. AI shuts down when actor is more than 7168 + //... At current time, this test is unnecessary. AI shuts down when actor is more than "actors processing range" setting value //... units from player, and exterior cells are 8192 units long and wide. //... But AI processing distance may increase in the future. if (isNearInactiveCell(pos)) @@ -354,7 +354,7 @@ bool MWMechanics::AiPackage::isNearInactiveCell(const ESM::Position& actorPos) // currently assumes 3 x 3 grid for exterior cells, with player at center cell. // ToDo: (Maybe) use "exterior cell load distance" setting to get count of actual active cells - // While AI Process distance is 7168, AI shuts down actors before they reach edges of 3 x 3 grid. + // AI shuts down actors before they reach edges of 3 x 3 grid. const float distanceFromEdge = 200.0; float minThreshold = (-1.0f * ESM::Land::REAL_SIZE) + distanceFromEdge; float maxThreshold = (2.0f * ESM::Land::REAL_SIZE) - distanceFromEdge; diff --git a/apps/openmw/mwmechanics/aitravel.cpp b/apps/openmw/mwmechanics/aitravel.cpp index 8b52b15a4..90beb9ead 100644 --- a/apps/openmw/mwmechanics/aitravel.cpp +++ b/apps/openmw/mwmechanics/aitravel.cpp @@ -3,8 +3,9 @@ #include #include -#include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" +#include "../mwbase/mechanicsmanager.hpp" +#include "../mwbase/world.hpp" #include "../mwworld/class.hpp" #include "../mwworld/cellstore.hpp" @@ -21,7 +22,8 @@ bool isWithinMaxRange(const osg::Vec3f& pos1, const osg::Vec3f& pos2) // Maximum travel distance for vanilla compatibility. // Was likely meant to prevent NPCs walking into non-loaded exterior cells, but for some reason is used in interior cells as well. // We can make this configurable at some point, but the default *must* be the below value. Anything else will break shoddily-written content (*cough* MW *cough*) in bizarre ways. - return (pos1 - pos2).length2() <= 7168*7168; + bool aiDistance = MWBase::Environment::get().getMechanicsManager()->getActorsProcessingRange(); + return (pos1 - pos2).length2() <= aiDistance*aiDistance; } } diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index ce844674f..d5e22cb07 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -30,6 +30,7 @@ #include "../mwrender/animation.hpp" #include "../mwbase/environment.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" @@ -1849,7 +1850,7 @@ void CharacterController::updateAnimQueue() mAnimation->setLoopingEnabled(mAnimQueue.front().mGroup, mAnimQueue.size() <= 1); } -void CharacterController::update(float duration) +void CharacterController::update(float duration, bool animationOnly) { MWBase::World *world = MWBase::Environment::get().getWorld(); const MWWorld::Class &cls = mPtr.getClass(); @@ -2235,10 +2236,10 @@ void CharacterController::update(float duration) world->rotateObject(mPtr, rot.x(), rot.y(), 0.0f, true); } - if (!mMovementAnimationControlled) + if (!animationOnly && !mMovementAnimationControlled) world->queueMovement(mPtr, vec); } - else + else if (!animationOnly) // We must always queue movement, even if there is none, to apply gravity. world->queueMovement(mPtr, osg::Vec3f(0.f, 0.f, 0.f)); @@ -2261,7 +2262,8 @@ void CharacterController::update(float duration) playDeath(1.f, mDeathState); } // We must always queue movement, even if there is none, to apply gravity. - world->queueMovement(mPtr, osg::Vec3f(0.f, 0.f, 0.f)); + if (!animationOnly) + world->queueMovement(mPtr, osg::Vec3f(0.f, 0.f, 0.f)); } bool isPersist = isPersistentAnimPlaying(); @@ -2295,7 +2297,7 @@ void CharacterController::update(float duration) moved.z() = 1.0; // Update movement - if(mMovementAnimationControlled && mPtr.getClass().isActor()) + if(!animationOnly && mMovementAnimationControlled && mPtr.getClass().isActor()) world->queueMovement(mPtr, moved); mSkipAnim = false; @@ -2544,20 +2546,6 @@ void CharacterController::updateMagicEffects() { if (!mPtr.getClass().isActor()) return; - float alpha = 1.f; - if (mPtr.getClass().getCreatureStats(mPtr).getMagicEffects().get(ESM::MagicEffect::Invisibility).getModifier()) // Ignore base magnitude (see bug #3555). - { - if (mPtr == getPlayer()) - alpha = 0.4f; - else - alpha = 0.f; - } - float chameleon = mPtr.getClass().getCreatureStats(mPtr).getMagicEffects().get(ESM::MagicEffect::Chameleon).getMagnitude(); - if (chameleon) - { - alpha *= std::max(0.2f, (100.f - chameleon)/100.f); - } - mAnimation->setAlpha(alpha); bool vampire = mPtr.getClass().getCreatureStats(mPtr).getMagicEffects().get(ESM::MagicEffect::Vampirism).getMagnitude() > 0.0f; mAnimation->setVampire(vampire); @@ -2566,6 +2554,32 @@ void CharacterController::updateMagicEffects() mAnimation->setLightEffect(light); } +void CharacterController::setVisibility(float visibility) +{ + // We should take actor's invisibility in account + if (mPtr.getClass().isActor()) + { + float alpha = 1.f; + if (mPtr.getClass().getCreatureStats(mPtr).getMagicEffects().get(ESM::MagicEffect::Invisibility).getModifier()) // Ignore base magnitude (see bug #3555). + { + if (mPtr == getPlayer()) + alpha = 0.4f; + else + alpha = 0.f; + } + float chameleon = mPtr.getClass().getCreatureStats(mPtr).getMagicEffects().get(ESM::MagicEffect::Chameleon).getMagnitude(); + if (chameleon) + { + alpha *= std::max(0.2f, (100.f - chameleon)/100.f); + } + + visibility = std::min(visibility, alpha); + } + + // TODO: implement a dithering shader rather than just change object transparency. + mAnimation->setAlpha(visibility); +} + void CharacterController::setAttackTypeBasedOnMovement() { float *move = mPtr.getClass().getMovementSettings(mPtr).mPosition; diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index f97614ef4..0f4b3aa90 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -257,7 +257,7 @@ public: void updatePtr(const MWWorld::Ptr &ptr); - void update(float duration); + void update(float duration, bool animationOnly=false); void persistAnimationState(); void unpersistAnimationState(); @@ -292,6 +292,7 @@ public: bool isTurning() const; bool isAttackingOrSpell() const; + void setVisibility(float visibility); void setAttackingOrSpell(bool attackingOrSpell); void castSpell(const std::string spellId, bool manualSpell=false); void setAIAttackType(const std::string& attackType); diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 619ed91b3..1c7576316 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -431,6 +431,25 @@ namespace MWMechanics mObjects.update(duration, paused); } + void MechanicsManager::processChangedSettings(const Settings::CategorySettingVector &changed) + { + for (Settings::CategorySettingVector::const_iterator it = changed.begin(); it != changed.end(); ++it) + { + if (it->first == "Game" && it->second == "actors processing range") + { + mActors.updateProcessingRange(); + + // Update mechanics for new processing range immediately + update(0.f, false); + } + } + } + + float MechanicsManager::getActorsProcessingRange() const + { + return mActors.getProcessingRange(); + } + bool MechanicsManager::isActorDetected(const MWWorld::Ptr& actor, const MWWorld::Ptr& observer) { return mActors.isActorDetected(actor, observer); diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index 3682e97ce..26eaa968d 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -1,6 +1,8 @@ #ifndef GAME_MWMECHANICS_MECHANICSMANAGERIMP_H #define GAME_MWMECHANICS_MECHANICSMANAGERIMP_H +#include + #include "../mwbase/mechanicsmanager.hpp" #include "../mwworld/ptr.hpp" @@ -206,6 +208,10 @@ namespace MWMechanics virtual void castSpell(const MWWorld::Ptr& ptr, const std::string spellId, bool manualSpell=false); + void processChangedSettings(const Settings::CategorySettingVector& settings) override; + + virtual float getActorsProcessingRange() const; + /// Check if the target actor was detected by an observer /// If the observer is a non-NPC, check all actors in AI processing distance as observers virtual bool isActorDetected(const MWWorld::Ptr& actor, const MWWorld::Ptr& observer); diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 2ce498f37..9ee47f3f5 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -1366,6 +1366,33 @@ namespace MWPhysics return false; } + void PhysicsSystem::setActorCollisionMode(const MWWorld::Ptr& ptr, bool enabled) + { + ActorMap::iterator found = mActors.find(ptr); + if (found != mActors.end()) + { + bool cmode = found->second->getCollisionMode(); + if (cmode == enabled) + return; + + cmode = enabled; + found->second->enableCollisionMode(cmode); + found->second->enableCollisionBody(cmode); + } + } + + bool PhysicsSystem::isActorCollisionEnabled(const MWWorld::Ptr& ptr) + { + ActorMap::iterator found = mActors.find(ptr); + if (found != mActors.end()) + { + bool cmode = found->second->getCollisionMode(); + return cmode; + } + + return false; + } + void PhysicsSystem::queueObjectMovement(const MWWorld::Ptr &ptr, const osg::Vec3f &movement) { PtrVelocityList::iterator iter = mMovementQueue.begin(); diff --git a/apps/openmw/mwphysics/physicssystem.hpp b/apps/openmw/mwphysics/physicssystem.hpp index 3ef9990f5..2de869395 100644 --- a/apps/openmw/mwphysics/physicssystem.hpp +++ b/apps/openmw/mwphysics/physicssystem.hpp @@ -86,6 +86,8 @@ namespace MWPhysics void removeHeightField (int x, int y); bool toggleCollisionMode(); + bool isActorCollisionEnabled(const MWWorld::Ptr& ptr); + void setActorCollisionMode(const MWWorld::Ptr& ptr, bool enabled); void stepSimulation(float dt); void debugDraw(); diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 6e2c76d1d..81701797d 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -1748,21 +1748,31 @@ namespace MWRender if (alpha != 1.f) { - osg::StateSet* stateset (new osg::StateSet); + // If we have an existing material for alpha transparency, just override alpha level + osg::StateSet* stateset = mObjectRoot->getOrCreateStateSet(); + osg::Material* material = static_cast(stateset->getAttribute(osg::StateAttribute::MATERIAL)); + if (material) + { + material->setAlpha(osg::Material::FRONT_AND_BACK, alpha); + } + else + { + osg::StateSet* stateset (new osg::StateSet); - osg::BlendFunc* blendfunc (new osg::BlendFunc); - stateset->setAttributeAndModes(blendfunc, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE); + osg::BlendFunc* blendfunc (new osg::BlendFunc); + stateset->setAttributeAndModes(blendfunc, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE); - // FIXME: overriding diffuse/ambient/emissive colors - osg::Material* material (new osg::Material); - material->setColorMode(osg::Material::OFF); - material->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(1,1,1,alpha)); - material->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(1,1,1,1)); - stateset->setAttributeAndModes(material, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE); + // FIXME: overriding diffuse/ambient/emissive colors + material = new osg::Material; + material->setColorMode(osg::Material::OFF); + material->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(1,1,1,alpha)); + material->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(1,1,1,1)); + stateset->setAttributeAndModes(material, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE); - mObjectRoot->setStateSet(stateset); + mObjectRoot->setStateSet(stateset); - mResourceSystem->getSceneManager()->recreateShaders(mObjectRoot); + mResourceSystem->getSceneManager()->recreateShaders(mObjectRoot); + } } else { diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 0f20fa05a..3b1d84a66 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1582,6 +1582,16 @@ namespace MWWorld } } + void World::setActorCollisionMode(const MWWorld::Ptr& ptr, bool enabled) + { + mPhysics->setActorCollisionMode(ptr, enabled); + } + + bool World::isActorCollisionEnabled(const MWWorld::Ptr& ptr) + { + return mPhysics->isActorCollisionEnabled(ptr); + } + bool World::toggleCollisionMode() { if (mPhysics->toggleCollisionMode()) @@ -3559,7 +3569,13 @@ namespace MWWorld MWBase::Environment::get().getMechanicsManager()->getObjectsInRange( origin, feetToGameUnits(static_cast(effectIt->mArea)), objects); for (std::vector::iterator affected = objects.begin(); affected != objects.end(); ++affected) + { + // Ignore actors without collisions here, otherwise it will be possible to hit actors outside processing range. + if (affected->getClass().isActor() && !isActorCollisionEnabled(*affected)) + continue; + toApply[*affected].push_back(*effectIt); + } } // Now apply the appropriate effects to each actor in range diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 1592453a2..9b027ec5a 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -407,6 +407,9 @@ namespace MWWorld bool castRay (float x1, float y1, float z1, float x2, float y2, float z2) override; + void setActorCollisionMode(const Ptr& ptr, bool enabled) override; + bool isActorCollisionEnabled(const Ptr& ptr) override; + bool toggleCollisionMode() override; ///< Toggle collision mode for player. If disabled player object should ignore /// collisions and gravity. diff --git a/docs/source/reference/modding/settings/game.rst b/docs/source/reference/modding/settings/game.rst index 4c3f4579f..254496d5b 100644 --- a/docs/source/reference/modding/settings/game.rst +++ b/docs/source/reference/modding/settings/game.rst @@ -97,8 +97,21 @@ and values above 500 will result in the player inflicting no damage. This setting can be controlled in game with the Difficulty slider in the Prefs panel of the Options menu. +actors processing range +----------------------- + +:Type: integer +:Range: 3584 to 7168 +:Default: 7168 + +This setting allows to specify a distance from player in game units, in which OpenMW updates actor's state. +Actor state update includes AI, animations, and physics processing. +Actors near that border start softly fade out instead of just appearing/disapperaing. + +This setting can be controlled in game with the "Actors processing range slider" in the Prefs panel of the Options menu. + classic reflected absorb spells behavior ------------------------------------------ +---------------------------------------- :Type: boolean :Range: True/False diff --git a/files/mygui/openmw_settings_window.layout b/files/mygui/openmw_settings_window.layout index 8ff850cae..8e6e98612 100644 --- a/files/mygui/openmw_settings_window.layout +++ b/files/mygui/openmw_settings_window.layout @@ -73,7 +73,32 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -83,7 +108,7 @@ - + @@ -93,7 +118,7 @@ - + @@ -103,7 +128,7 @@ - + diff --git a/files/settings-default.cfg b/files/settings-default.cfg index ea6f6e7b6..66c6c6577 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -200,6 +200,9 @@ best attack = false # Difficulty. Expressed as damage dealt and received. (e.g. -100 to 100). difficulty = 0 +# The maximum range of actor AI, animations and physics updates. +actors processing range = 7168 + # Make reflected Absorb spells have no practical effect, like in Morrowind. classic reflected absorb spells behavior = true From bf9e8c4556e3f545239dfc3c061b8b8baaa98aef Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Fri, 26 Oct 2018 12:36:58 +0400 Subject: [PATCH 140/163] Make spell absorption multiplicative (bug #4684) --- CHANGELOG.md | 1 + apps/openmw/mwmechanics/spellcasting.cpp | 40 ++++++++++++++++++++++-- 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 07c0217da..9fa330e77 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -143,6 +143,7 @@ Bug #4674: Journal can be opened when settings window is open Bug #4677: Crash in ESM reader when NPC record has DNAM record without DODT one Bug #4678: Crash in ESP parser when SCVR has no variable names + Bug #4684: Spell Absorption is additive Bug #4685: Missing sound causes an exception inside Say command Bug #4689: Default creature soundgen entries are not used Feature #912: Editor: Add missing icons to UniversalId tables diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index a91aa2f31..4a74e1647 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -324,6 +324,34 @@ namespace MWMechanics return true; } + class GetAbsorptionProbability : public MWMechanics::EffectSourceVisitor + { + public: + float mProbability; + + GetAbsorptionProbability(const MWWorld::Ptr& actor) + : mProbability(0.f){} + + virtual void visit (MWMechanics::EffectKey key, + const std::string& sourceName, const std::string& sourceId, int casterActorId, + float magnitude, float remainingTime = -1, float totalTime = -1) + { + if (key.mId == ESM::MagicEffect::SpellAbsorption) + { + if (mProbability == 0.f) + mProbability = magnitude / 100; + else + { + // If there are different sources of SpellAbsorption effect, multiply failing probability for all effects. + // Real absorption probability will be the (1 - total fail chance) in this case. + float failProbability = 1.f - mProbability; + failProbability *= 1.f - magnitude / 100; + mProbability = 1.f - failProbability; + } + } + } + }; + CastSpell::CastSpell(const MWWorld::Ptr &caster, const MWWorld::Ptr &target, const bool fromProjectile, const bool manualSpell) : mCaster(caster) , mTarget(target) @@ -444,12 +472,18 @@ namespace MWMechanics MWBase::Environment::get().getWindowManager()->setEnemy(target); // Try absorbing if it's a spell - // NOTE: Vanilla does this once per spell absorption effect source instead of adding the % from all sources together, not sure - // if that is worth replicating. + // NOTE: Vanilla does this once per spell absorption effect source instead of adding the % from all sources together, so use the same approach here bool absorbed = false; if (spell && caster != target && target.getClass().isActor()) { - float absorb = target.getClass().getCreatureStats(target).getMagicEffects().get(ESM::MagicEffect::SpellAbsorption).getMagnitude(); + GetAbsorptionProbability check(target); + MWMechanics::CreatureStats& stats = target.getClass().getCreatureStats(target); + stats.getActiveSpells().visitEffectSources(check); + stats.getSpells().visitEffectSources(check); + if (target.getClass().hasInventoryStore(target)) + target.getClass().getInventoryStore(target).visitEffectSources(check); + + int absorb = check.mProbability * 100; absorbed = (Misc::Rng::roll0to99() < absorb); if (absorbed) { From 6d91fe69b268e6b2a8fc5da4a4432a5de346195d Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Fri, 26 Oct 2018 12:51:04 +0400 Subject: [PATCH 141/163] Revert "Handle RootCollisionNode, attached to non-root node (bug #4311)" This reverts commit ec9a1b0d0526c9aba7d7cdd0e68789ab0e747d50. --- CHANGELOG.md | 1 - components/nifbullet/bulletnifloader.cpp | 42 ++++++++---------------- components/nifbullet/bulletnifloader.hpp | 4 +-- 3 files changed, 16 insertions(+), 31 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 07c0217da..0f668010d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -57,7 +57,6 @@ 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 Bug #4333: Keyboard navigation in containers is not intuitive Bug #4358: Running animation is interrupted when magic mode is toggled diff --git a/components/nifbullet/bulletnifloader.cpp b/components/nifbullet/bulletnifloader.cpp index 7b206e40c..c18b96d39 100644 --- a/components/nifbullet/bulletnifloader.cpp +++ b/components/nifbullet/bulletnifloader.cpp @@ -96,17 +96,14 @@ osg::ref_ptr BulletNifLoader::load(const Nif::File& nif) } 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 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(filename, rootCollisionNode, 0, false, isAnimated, false); - else - handleNode(filename, node, 0, true, isAnimated, true); + handleNode(node, 0, autogenerated, isAnimated, autogenerated); if (mCompoundShape) { @@ -163,34 +160,25 @@ bool BulletNifLoader::findBoundingBox(const Nif::Node* node, int flags) return false; } -const Nif::Node* BulletNifLoader::getCollisionNode(const Nif::Node* rootNode) +bool BulletNifLoader::hasAutoGeneratedCollision(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; - if (list.length() == 1 && - rootNode->recType == Nif::RC_NiNode && - list[0].getPtr()->recType == Nif::RC_NiNode) + for(size_t i = 0;i < list.length();i++) { - 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; + if(!list[i].empty()) + { + if(list[i].getPtr()->recType == Nif::RC_RootCollisionNode) + return false; + } } } - return nullptr; + return true; } -void BulletNifLoader::handleNode(const std::string& fileName, const Nif::Node *node, int flags, +void BulletNifLoader::handleNode(const Nif::Node *node, int flags, bool isCollisionNode, bool isAnimated, bool autogenerated) { // Accumulate the flags from all the child nodes. This works for all @@ -203,9 +191,6 @@ 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) - 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) flags |= 0x800; @@ -234,6 +219,7 @@ void BulletNifLoader::handleNode(const std::string& fileName, const Nif::Node *n // Marker can still have collision if the model explicitely specifies it via a RootCollisionNode. return; } + } } @@ -256,7 +242,7 @@ void BulletNifLoader::handleNode(const std::string& fileName, const Nif::Node *n for(size_t i = 0;i < list.length();i++) { if(!list[i].empty()) - handleNode(fileName, list[i].getPtr(), flags, isCollisionNode, isAnimated, autogenerated); + handleNode(list[i].getPtr(), flags, isCollisionNode, isAnimated, autogenerated); } } } diff --git a/components/nifbullet/bulletnifloader.hpp b/components/nifbullet/bulletnifloader.hpp index f970b7f3e..cc9aaa844 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(const std::string& fileName, Nif::Node const *node, int flags, bool isCollisionNode, bool isAnimated=false, bool autogenerated=false); + void handleNode(Nif::Node const *node, int flags, bool isCollisionNode, bool isAnimated=false, bool autogenerated=false); - const Nif::Node* getCollisionNode(const Nif::Node* rootNode); + bool hasAutoGeneratedCollision(const Nif::Node *rootNode); void handleNiTriShape(const Nif::NiTriShape *shape, int flags, const osg::Matrixf& transform, bool isAnimated); From 19ce1abcdf40d196ae88d242e5b319f03d7a05ab Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Fri, 26 Oct 2018 12:53:15 +0300 Subject: [PATCH 142/163] Fix selected weapon HUD durability percentage --- apps/openmw/mwgui/windowmanagerimp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 5ffed4815..393751855 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -1385,7 +1385,7 @@ namespace MWGui int durabilityPercent = 100; if (item.getClass().hasItemHealth(item)) { - durabilityPercent = static_cast(item.getClass().getItemNormalizedHealth(item)); + durabilityPercent = static_cast(item.getClass().getItemNormalizedHealth(item) * 100); } mHud->setSelectedWeapon(item, durabilityPercent); mInventoryWindow->setTitle(item.getClass().getName(item)); From 61da6b6ecfbeb08a405e2ebd620c3bcbc77ea37d Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Fri, 26 Oct 2018 13:24:05 +0400 Subject: [PATCH 143/163] Print warning if the RootCollisionNode is attached to non-root node (bug #4311) --- CHANGELOG.md | 1 + components/nifbullet/bulletnifloader.cpp | 10 +++++++--- components/nifbullet/bulletnifloader.hpp | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f668010d..07c0217da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -57,6 +57,7 @@ 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 Bug #4333: Keyboard navigation in containers is not intuitive Bug #4358: Running animation is interrupted when magic mode is toggled diff --git a/components/nifbullet/bulletnifloader.cpp b/components/nifbullet/bulletnifloader.cpp index c18b96d39..20307f252 100644 --- a/components/nifbullet/bulletnifloader.cpp +++ b/components/nifbullet/bulletnifloader.cpp @@ -103,7 +103,7 @@ osg::ref_ptr BulletNifLoader::load(const Nif::File& nif) const auto filename = nif.getFilename(); const bool isAnimated = pathFileNameStartsWithX(filename); - handleNode(node, 0, autogenerated, isAnimated, autogenerated); + handleNode(filename, node, 0, autogenerated, isAnimated, autogenerated); if (mCompoundShape) { @@ -178,7 +178,7 @@ bool BulletNifLoader::hasAutoGeneratedCollision(const Nif::Node* rootNode) return true; } -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 @@ -195,6 +195,10 @@ void BulletNifLoader::handleNode(const Nif::Node *node, int flags, if(node->recType == Nif::RC_AvoidNode) flags |= 0x800; + // We encountered a RootCollisionNode inside autogenerated mesh. It is not right. + if (node->recType == Nif::RC_RootCollisionNode && autogenerated) + Log(Debug::Info) << "Found RootCollisionNode attached to non-root node in " << fileName << ". Treat it as a common NiTriShape."; + // Check for extra data Nif::Extra const *e = node; while (!e->extra.empty()) @@ -242,7 +246,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 cc9aaa844..f67ab402e 100644 --- a/components/nifbullet/bulletnifloader.hpp +++ b/components/nifbullet/bulletnifloader.hpp @@ -55,7 +55,7 @@ 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); From d2f3196ee861907b5b7048792726a64bf045fb9f Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Fri, 26 Oct 2018 21:21:34 +0400 Subject: [PATCH 144/163] Fix testcase for RootCollisionNode with MRK data --- apps/openmw_test_suite/nifloader/testbulletnifloader.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/apps/openmw_test_suite/nifloader/testbulletnifloader.cpp b/apps/openmw_test_suite/nifloader/testbulletnifloader.cpp index a2311be49..2361718b1 100644 --- a/apps/openmw_test_suite/nifloader/testbulletnifloader.cpp +++ b/apps/openmw_test_suite/nifloader/testbulletnifloader.cpp @@ -929,10 +929,8 @@ namespace 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; + mNiNode2.children = Nif::NodeList(std::vector({Nif::NodePtr(&mNiTriShape)})); + mNiNode2.recType = Nif::RC_RootCollisionNode; mNiNode.children = Nif::NodeList(std::vector({Nif::NodePtr(&mNiNode2)})); mNiNode.recType = Nif::RC_NiNode; From f62ca24356a16c7148bef07bdaf2ba19464f2236 Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Fri, 26 Oct 2018 19:33:04 +0200 Subject: [PATCH 145/163] [macOS, CI] Use dependencies with downgraded MyGUI, fixing #4665 --- CI/before_install.osx.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CI/before_install.osx.sh b/CI/before_install.osx.sh index 2ab996b10..51b4b90be 100755 --- a/CI/before_install.osx.sh +++ b/CI/before_install.osx.sh @@ -6,5 +6,5 @@ brew outdated cmake || brew upgrade cmake brew outdated pkgconfig || brew upgrade pkgconfig brew install qt -curl -fSL -R -J https://downloads.openmw.org/osx/dependencies/openmw-deps-100d2e0.zip -o ~/openmw-deps.zip +curl -fSL -R -J https://downloads.openmw.org/osx/dependencies/openmw-deps-4eec887.zip -o ~/openmw-deps.zip unzip -o ~/openmw-deps.zip -d /private/tmp/openmw-deps > /dev/null From 4873d33642ca6973a266244e3b73c40da5f6c6a0 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 27 Oct 2018 01:25:16 +0300 Subject: [PATCH 146/163] Adjust magic light source linear attenuation (bug #3890) --- CHANGELOG.md | 1 + apps/openmw/mwrender/animation.cpp | 12 ++++++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9fa330e77..ed387eaf6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,7 @@ Bug #3788: GetPCInJail and GetPCTraveling do not work as in vanilla Bug #3836: Script fails to compile when command argument contains "\n" Bug #3876: Landscape texture painting is misaligned + Bug #3890: Magic light source attenuation is inaccurate 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 #3920: RemoveSpellEffects doesn't remove constant effects diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index f165b6bd3..8ae8800a6 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -1789,9 +1789,12 @@ namespace MWRender } else { - effect += 3; - float radius = effect * 66.f; - float linearAttenuation = 0.5f / effect; + // TODO: use global attenuation settings + + // 1 pt of Light effect magnitude corresponds to 1 foot of light source radius, which is about 21.33 game units, + // but Morrowind uses imprecise value of foot for magic effects. + float radius = effect * 22.f; + float linearAttenuation = 3.f / radius; if (!mGlowLight || linearAttenuation != mGlowLight->getLight(0)->getLinearAttenuation()) { @@ -1813,7 +1816,8 @@ namespace MWRender mGlowLight->setLight(light); } - mGlowLight->setRadius(radius); + // Make the obvious cut-off a bit less obvious + mGlowLight->setRadius(radius * 3); } } From 89f4ad18356353b2c1ddca2a7ef0a671ab624c81 Mon Sep 17 00:00:00 2001 From: Alec Nunn Date: Fri, 26 Oct 2018 21:52:56 -0700 Subject: [PATCH 147/163] Worked with formatting the documentation source to conform to standards desired. Also corrected numerous spelling errors and inconsistencies. --- docs/source/reference/documentationHowTo.rst | 102 +++++++++++++---- docs/source/reference/modding/differences.rst | 26 +++-- docs/source/reference/modding/font.rst | 17 ++- docs/source/reference/modding/foreword.rst | 8 +- docs/source/reference/modding/mod-install.rst | 10 +- .../source/reference/modding/settings/HUD.rst | 3 +- .../reference/modding/settings/cells.rst | 20 +++- .../reference/modding/settings/game.rst | 22 ++-- .../source/reference/modding/settings/map.rst | 7 +- .../reference/modding/settings/saves.rst | 7 +- .../reference/modding/settings/shaders.rst | 5 +- .../reference/modding/settings/sound.rst | 4 +- .../reference/modding/settings/terrain.rst | 3 +- .../reference/modding/settings/video.rst | 6 +- .../reference/modding/settings/windows.rst | 5 +- .../convert-bump-mapped-mods.rst | 106 ++++++++++++++---- .../texture-modding/texture-basics.rst | 10 +- 17 files changed, 273 insertions(+), 88 deletions(-) diff --git a/docs/source/reference/documentationHowTo.rst b/docs/source/reference/documentationHowTo.rst index b27c122cb..75dbe8dca 100644 --- a/docs/source/reference/documentationHowTo.rst +++ b/docs/source/reference/documentationHowTo.rst @@ -8,9 +8,19 @@ Or a beginner's guide to writing docs without having to deal with more techie st Intro ===== -The premise of this guide is that you would like to help out the OpenMW project beyond play-testing for bugs and such, buuuuut you're like me and don't really know how to code. This has the rather pesky side effect of you not really knowing about all the tools like GitHub and such. While many of these tools are super handy and great to know how to use, not everyone has the actual need and desire to learn the ins and outs of them. Since we would like as much help fleshing out the user documentation as possible, I wrote this guide to lower the barrier of entry into contributing to the project. +The premise of this guide is that you would like to help out the OpenMW project beyond play-testing for bugs and such, +*buuuuut* you're like me and don't really know how to code. +This has the rather pesky side effect of you not really knowing about all the tools like GitHub and such. +While many of these tools are super handy and great to know how to use, +not everyone has the actual need and desire to learn the ins and outs of them. +Since we would like as much help fleshing out the user documentation as possible, +I wrote this guide to lower the barrier of entry into contributing to the project. -*However*, as much as I will try to guide you through all the tedious setup and day-to-day stuff, you will eventually have to learn to write using ReST (reStructuredText) formatting. Since you're probably like me when I started helping and don't know wtf ReST is, never fear. It's an incredibly simple language that is easy to read in plain text form that can then be converted automatically into different types of documents like PDFs and html for webpages. +*However*, as much as I will try to guide you through all the tedious setup and day-to-day stuff, +you will eventually have to learn to write using ReST (reStructuredText) formatting. +Since you're probably like me when I started helping and don't know wtf ReST is, never fear. +It's an incredibly simple language that is easy to read in plain text form that can then be converted automatically +into different types of documents like PDFs and html for webpages. Baby Steps ========== @@ -25,9 +35,11 @@ Choose Repository and Files in the menu on the left, then docs and source in the Don’t overlook the tutorial-style-guide.txt there for some tips to get you started. Open whichever file you want to tackle – probably within the manuals or reference directories. -There’s also a dropdown box to the right of edit, at the top of the left menu, which offers options such as new file or directory, or upload file, with “+” to close that dropdown box. +There’s also a dropdown box to the right of edit, at the top of the left menu, +which offers options such as new file or directory, or upload file, with “+” to close that dropdown box. -Click on "Edit" towards top right which will reveal the underlying version, rather than the version displayed to normal reaaders. Use "Write" and "Preview" to switch between the two views. +Click on "Edit" towards top right which will reveal the underlying version, +rather than the version displayed to normal readers. Use "Write" and "Preview" to switch between the two views. When you have made the appropriate changes, and checked them in Preview mode, click the Green "Commit changes" button at the bottom. This should add a branch, with a default name such as patch-1, to your own repository, and add a Merge Request to the main OpenMW Project. @@ -48,13 +60,24 @@ So here's what you're gonna be learning how to set up: GitHub ====== -GitHub is the website the OpenMW project is hosted on. It utilizes Git, which is a version control system, meaning it helps us all collaborate on the project without interfering with each others' work. The commands are a little annoying because there is a certain amount of undescriptive jargon, but for the most part, what you need to know is very simple and I'll walk you through it. There are three main parts that you should know: +GitHub is the website the OpenMW project is hosted on. It utilizes Git, which is a version control system, +meaning it helps us all collaborate on the project without interfering with each others' work. +The commands are a little annoying because there is a certain amount of undescriptive jargon, +but for the most part, what you need to know is very simple and I'll walk you through it. +There are three main parts that you should know: 1. The OpenMW repository 2. Your online repository 3. Your local repository -The master OpenMW respository is where all of our work comes together and where the most current version of the source code resides. A repository, also called repo, is a directory or the main folder that holds a project. You will need to create your own account on GitHub so you can *fork* the OpenMW repository. Forking is just when you clone a project into a repository on your own account so you can make changes however you like without accidentally messing up the original project. Now, you could add and edit files on GitHub.com directly through your online repository, however it's much easier to work on them on your own computer in your local repository. Local just refers to the fact that it's physically stored on your computer's hard drive. Here are the easy steps for doing all this: +The master OpenMW repository is where all of our work comes together and where the most current version of the source code resides. +A repository, also called repo, is a directory or the main folder that holds a project. +You will need to create your own account on GitHub so you can *fork* the OpenMW repository. +Forking is just when you clone a project into a repository on your own account so you can make changes however you like +without accidentally messing up the original project. +Now, you could add and edit files on GitHub.com directly through your online repository, +however it's much easier to work on them on your own computer in your local repository. +Local just refers to the fact that it's physically stored on your computer's hard drive. Here are the easy steps for doing all this: 1. Go to GitHub.com and sign up for a free account. 2. Navigate to the master OpenMW repo at: https://github.com/OpenMW/openmw @@ -67,7 +90,11 @@ If you want more info I recommend reading this guide: https://readwrite.com/2013 PyCharm ======= -PyCharm is what's known as an IDE, which stands for integrated development environment. All this means is that it's for writing code and has a bunch of built-in features that make it easier to do so. In this case, PyCharm is made for the language Python, which is what Sphinx is written in. We won't actually be touching any of the Python, but some of the built-in features are extremely useful. Let's start setting it up: +PyCharm is what's known as an IDE, which stands for integrated development environment. +All this means is that it's for writing code and has a bunch of built-in features that make it easier to do so. +In this case, PyCharm is made for the language Python, which is what Sphinx is written in. +We won't actually be touching any of the Python, but some of the built-in features are extremely useful. +Let's start setting it up: 1. Go to https://www.jetbrains.com/pycharm/download/ 2. Select your OS, then download the free Community version. @@ -81,31 +108,61 @@ PyCharm is what's known as an IDE, which stands for integrated development envir 10. Back in the welcome window, click "Check out from version control" and select GitHub. .. note:: - After this step, it should log in to your GitHub. If not, you probably messed up the Token creation. If you're on Mac, you may come across and error complaining about XCode and admin priviledges. If this happens, open Terminal and type: ``sudo xcodebuild -license`` Read through the license and agree. This should fix the error and allow you to log in. + After this step, it should log in to your GitHub. If not, you probably messed up the Token creation. + If you're on Mac, you may come across and error complaining about XCode and admin priviledges. If this happens, + open Terminal and type: ``sudo xcodebuild -license`` Read through the license and agree. + This should fix the error and allow you to log in. 11. In Git Repository URL, select your OpenMW repository and click Clone -Congrats! You now have the OpenMW sourcecode on your computer and you can begin making changes and contributing. If you're reading this guide though, you probably won't have any idea how to do that, so let's go through setting up Sphinx, then I'll go through it. +Congrats! You now have the OpenMW source code on your computer and you can begin making changes and contributing. +If you're reading this guide though, you probably won't have any idea how to do that, +so let's go through setting up Sphinx, then I'll go through it. Sphinx ====== -So far I've mentioned ReST (reStructuredText) a couple times, but what is it, and what is Sphinx? The most basic explanation is that ReST is the markup language (like HTML is the markup language for webpages) and Sphinx is the program that goes through and builds the actual document so you can read it in a more visually pleasing way. For a much more detailed explanation, I recommend: https://coderwall.com/p/vemncg/what-is-the-difference-rest-docutils-sphinx-readthedocs +So far I've mentioned ReST (reStructuredText) a couple times, but what is it, and what is Sphinx? +The most basic explanation is that ReST is the markup language (like HTML is the markup language for webpages) +and Sphinx is the program that goes through and builds the actual document so you can read it in a more visually pleasing way. +For a much more detailed explanation, I recommend: https://coderwall.com/p/vemncg/what-is-the-difference-rest-docutils-sphinx-readthedocs -This will be the most technical section as we have to use the command prompt or terminal to install Python and Sphinx. I had intended to give you a universal explanation on how to install both, but it would drastically increase the length of this guide. The tutorial on the Sphinx website is really just going to be better than anything I write here, so please refer to their guide here: https://www.sphinx-doc.org/en/stable/install.html +This will be the most technical section as we have to use the command prompt or terminal to install Python and Sphinx. +I had intended to give you a universal explanation on how to install both, +but it would drastically increase the length of this guide. +The tutorial on the Sphinx website is really just going to be better than anything I write here, +so please refer to their guide here: https://www.sphinx-doc.org/en/stable/install.html Hopefully you now have Python and Sphinx installed. ... -Now you should have everything installed and running so you can collaborate on documentation properly. Let's go through a few more brief GitHub basics. There are really only 4 things you will be using regularly: +Now you should have everything installed and running so you can collaborate on documentation properly. +Let's go through a few more brief GitHub basics. There are really only 4 things you will be using regularly: 1. Rebase 2. Commit 3. Push 4. Pull request (PR) -Rebasing means you're taking all changes in one branch and applying them directly on top of another branch. This is slightly different than a merge which compares the two branches and makes another state combining the two. The difference is slight, but we use the rebase because it keeps the history cleaner. You will always rebase your local repository from the OpenMW master repository. This ensures you have all the most up to date changes before working on stuff so there is less chance of conflicts that need to be resolved when your branch is merged back into the master. A commit is basically just stating which files you want to mark as ready to be "pushed" to your online repository. A push is just copying those "committed" changes to your online repo. (Commit and push can be combined in one step in PyCharm, so yay) Once you've pushed all the changes you need to contribute something to the project, you will then submit a pull request, so called because you are *requesting* that the project maintainers "pull" and merge the changes you've made into the project master repository. One of the project maintainers will probably ask you to make some corrections or clarifications. Go back and repeat this process to make those changes, and repeat until they're good enough to get merged. +Rebasing means you're taking all changes in one branch and applying them directly on top of another branch. +This is slightly different than a merge which compares the two branches and makes another state combining the two. +The difference is slight, but we use the rebase because it keeps the history cleaner. +You will always rebase your local repository from the OpenMW master repository. +This ensures you have all the most up to date changes before working on stuff so there is less chance of conflicts that +need to be resolved when your branch is merged back into the master. +A commit is basically just stating which files you want to mark as ready to be "pushed" to your online repository. +A push is just copying those "committed" changes to your online repo. +(Commit and push can be combined in one step in PyCharm, so yay) +Once you've pushed all the changes you need to contribute something to the project, you will then submit a pull request, +so called because you are *requesting* that the project maintainers "pull" + and merge the changes you've made into the project master repository. One of the project maintainers will probably ask + you to make some corrections or clarifications. Go back and repeat this process to make those changes, + and repeat until they're good enough to get merged. -So to go over all that again. You rebase *every* time you start working on something to ensure you're working on the most updated version (I do literally every time I open PyCharm). Then make your edits. You commit and push from your local repo to your online repo. Then you submit a pull request and people can review your changes before they get merged into the project master! Or in list form: +So to go over all that again. You rebase *every* time you start working on something to ensure you're working on the most +updated version (I do literally every time I open PyCharm). Then make your edits. +You commit and push from your local repo to your online repo. +Then you submit a pull request and people can review your changes before they get merged into the project master! +Or in list form: 1. Rebase local repo from OpenMW master 2. Make your edits @@ -116,7 +173,9 @@ So to go over all that again. You rebase *every* time you start working on somet Preview Documentation ********************* -You will probably find it helpful to be able to preview any documentation you've made. I often forget necessary syntax and this allows me to double check my work before submitting a PR. Luckily, PyCharm has a handy built-in feature that allows you to easily generate the docs. +You will probably find it helpful to be able to preview any documentation you've made. +I often forget necessary syntax and this allows me to double check my work before submitting a PR. +Luckily, PyCharm has a handy built-in feature that allows you to easily generate the docs. 1. In the top right corner of the PyCharm window, select the drop-down menu and select `Edit Configurations`. 2. In the `Run/Debug Configurations` dialogue, click the green plus button in the top left and select `Python Docs > Sphinx Tasks`. @@ -127,16 +186,21 @@ You will probably find it helpful to be able to preview any documentation you've :Output: 4. Click `Apply`, then `OK`. -Now in order to generate the documentation on your computer to preview them, just click the green play button in the top right, next to the drop down menu with the name you chose above selected. Sphinx will run and you can view the resulting documentation wherever you chose Output to be, above. The window that Sphinx runs in will also show any errors that occur during the build in red, which should help you find typos and missing/incorrect syntax. +Now in order to generate the documentation on your computer to preview them, +just click the green play button in the top right, next to the drop down menu with the name you chose above selected. +Sphinx will run and you can view the resulting documentation wherever you chose Output to be, above. +The window that Sphinx runs in will also show any errors that occur during the build in red, +which should help you find typos and missing/incorrect syntax. GitLab integration in PyCharm ============================= -As most of the hosting of OpenMW has moved to Gitlab, we should encourage the use of GitLab, though GitHub will continue to be supported. +As most of the hosting of OpenMW has moved to Gitlab, we should encourage the use of GitLab, +though GitHub will continue to be supported. -Add a couple of plugins to Pycharm - see general instructions at https://www.jetbrains.com/help/pycharm/installing-updating-and-uninstalling-repository-plugins.html +Add a couple of plugins to PyCharm - see general instructions at https://www.jetbrains.com/help/pycharm/installing-updating-and-uninstalling-repository-plugins.html -For Linux/Windows - (Macos is a little different) +For Linux/Windows - (MacOS is a little different) 1. File/Settings/Plugins 2. Browse Repositories diff --git a/docs/source/reference/modding/differences.rst b/docs/source/reference/modding/differences.rst index d492dc542..5533a3aa3 100644 --- a/docs/source/reference/modding/differences.rst +++ b/docs/source/reference/modding/differences.rst @@ -4,15 +4,19 @@ Modding OpenMW vs Morrowind A brief overview of the differences between the two engines. ============================================================ -OpenMW is designed to be able to use all the normal Morrowind mod files such as ESM/ESP plugins, texture replacers, mesh replacers, etc. +OpenMW is designed to be able to use all the normal Morrowind mod files such as ESM/ESP plugins, texture replacers, +mesh replacers, etc. .. warning:: - All external programs and libraries that depend on ``morrowind.exe`` cannot function with OpenMW. This means you should assume mods dependent on Morrowind Graphics Extender, Morrowind Code Patch, Morrowind Script Extender, etc, will *not* work correctly, nor will the tools themselves. + All external programs and libraries that depend on ``morrowind.exe`` cannot function with OpenMW. + This means you should assume mods dependent on Morrowind Graphics Extender, Morrowind Code Patch, + Morrowind Script Extender, etc, will *not* work correctly, nor will the tools themselves. Multiple Data Folders --------------------- -The largest difference between OpenMW and Morrowind in terms of data structure is OpenMW's support of multiple data folders. This has many advantages, especially when it comes to uninstalling mods and preventing unintentional overwrites of files. +The largest difference between OpenMW and Morrowind in terms of data structure is OpenMW's support of multiple data folders. +This has many advantages, especially when it comes to uninstalling mods and preventing unintentional overwrites of files. .. warning:: Most mods can still be installed into the root OpenMW data folder, but this is not recommended. @@ -34,25 +38,33 @@ To uninstall these mods simply delete that mod's respective ``data=`` entry. The mods are loaded in the order of these entries, with the top being overwritten by mods added towards the bottom. .. note:: - Mods that depend on ESM/ESP plugins can be rearranged within the OpenMW Launcher, but mesh/texture replacer mods can only be reordered by moving their ``data=`` entry. + Mods that depend on ESM/ESP plugins can be rearranged within the OpenMW Launcher, + but mesh/texture replacer mods can only be reordered by moving their ``data=`` entry. OpenMW Launcher --------------- -The launcher included with OpenMW is similar to the original Morrowind Launcher. Go to the Data Files tab to enable and disable plugins. You can also drag list items to modify the load order. Content lists can be created at the bottom by clicking the New Content List button, creating a list name, then setting up a new modlist. This is helpful for different player profiles and testing out different load orders. +The launcher included with OpenMW is similar to the original Morrowind Launcher. +Go to the Data Files tab to enable and disable plugins. You can also drag list items to modify the load order. +Content lists can be created at the bottom by clicking the New Content List button, creating a list name, +then setting up a new modlist. This is helpful for different player profiles and testing out different load orders. .. TODO use a substitution image for the New Content List button. Settings.cfg ------------ -The ``settings.cfg`` file is essentially the same as the INI files for Morrowind. It is located in the same directory as ``openmw.cfg``. This is where many video, audio, GUI, input, etc. settings can be modified. Some are available in-game, but many are only available in this configuration file. Please see https://wiki.openmw.org/index.php?title=Settings for the complete listing. +The ``settings.cfg`` file is essentially the same as the INI files for Morrowind. +It is located in the same directory as ``openmw.cfg``. This is where many video, audio, GUI, input, etc. +settings can be modified. Some are available in-game, but many are only available in this configuration file. +Please see https://wiki.openmw.org/index.php?title=Settings for the complete listing. .. TODO Create a proper ReST document tree for all the settings rather than Wiki. Open Source Resources Support ----------------------------- -While OpenMW supports all of the original files that Morrowind supported, we've expanded support to many open source file formats. These are summarized below: +While OpenMW supports all of the original files that Morrowind supported, +we've expanded support to many open source file formats. These are summarized below: diff --git a/docs/source/reference/modding/font.rst b/docs/source/reference/modding/font.rst index 80d01c27f..213cdf760 100644 --- a/docs/source/reference/modding/font.rst +++ b/docs/source/reference/modding/font.rst @@ -4,14 +4,20 @@ Fonts Morrowind .fnt fonts -------------------- -Morrowind uses a custom ``.fnt`` file format. It is not compatible with the Windows Font File ``.fnt`` format, nor compatible with ``.fnt`` formats from any other Bethesda games. To our knowledge, the format is undocumented and no tools for viewing or editing these fonts exist. +Morrowind uses a custom ``.fnt`` file format. It is not compatible with the Windows Font File ``.fnt`` format, +nor compatible with ``.fnt`` formats from any other Bethesda games. To our knowledge, +the format is undocumented and no tools for viewing or editing these fonts exist. -OpenMW can load this format and convert it on the fly into something usable (see font loader `source code `_). In OpenMW 0.32, an --export-fonts command line option was added to write the converted font (a PNG image and an XML file describing the position of each glyph in the image) to the current directory. +OpenMW can load this format and convert it on the fly into something usable +(see font loader `source code `_). +In OpenMW 0.32, an --export-fonts command line option was added to write the converted font +(a PNG image and an XML file describing the position of each glyph in the image) to the current directory. TrueType fonts -------------- -Unlike vanilla Morrowind, OpenMW directly supports TrueType (``.ttf``) fonts. This is the recommended way to create new fonts. +Unlike vanilla Morrowind, OpenMW directly supports TrueType (``.ttf``) fonts. +This is the recommended way to create new fonts. - To replace the primary "Magic Cards" font: @@ -74,4 +80,7 @@ Unlike vanilla Morrowind, OpenMW directly supports TrueType (``.ttf``) fonts. Th Bitmap fonts ------------ -Morrowind ``.fnt`` files are essentially a bitmap font, but using them is discouraged because of no Unicode support. MyGUI has its own format for bitmap fonts. An example can be seen by using the --export-fonts command line option (see above), which converts Morrowind ``.fnt`` to a MyGUI bitmap font. This is the recommended format to use if you wish to edit Morrowind's bitmap font or create a new bitmap font. +Morrowind ``.fnt`` files are essentially a bitmap font, but using them is discouraged because of no Unicode support. +MyGUI has its own format for bitmap fonts. An example can be seen by using the --export-fonts command line option (see above), +which converts Morrowind ``.fnt`` to a MyGUI bitmap font. +This is the recommended format to use if you wish to edit Morrowind's bitmap font or create a new bitmap font. diff --git a/docs/source/reference/modding/foreword.rst b/docs/source/reference/modding/foreword.rst index cf72b2aa3..30b5fdb3b 100644 --- a/docs/source/reference/modding/foreword.rst +++ b/docs/source/reference/modding/foreword.rst @@ -1,4 +1,10 @@ Foreword ######## -OpenMW is a complete game engine built to be content agnostic. The majority of this guide is applicable to any non-Morrowind project using its engine. That being said, it was designed with the extensive modding community of Morrowind in mind. Therefore, if you are already familiar with modding in Morrowind, you will likely be able to start modding in OpenMW with little to no instruction. We do recommend you at least refer to :doc:`differences` to find out about what's different between OpenMW and the original Morrowind engine. For everyone else, or just a good refresher, read on! \ No newline at end of file +OpenMW is a complete game engine built to be content agnostic. +The majority of this guide is applicable to any non-Morrowind project using its engine. +That being said, it was designed with the extensive modding community of Morrowind in mind. +Therefore, if you are already familiar with modding in Morrowind, +you will likely be able to start modding in OpenMW with little to no instruction. +We do recommend you at least refer to :doc:`differences` to find out about what's different between OpenMW and the +original Morrowind engine. For everyone else, or just a good refresher, read on! \ No newline at end of file diff --git a/docs/source/reference/modding/mod-install.rst b/docs/source/reference/modding/mod-install.rst index e62c27fc1..2c883aa59 100644 --- a/docs/source/reference/modding/mod-install.rst +++ b/docs/source/reference/modding/mod-install.rst @@ -21,16 +21,20 @@ Install #. If your mod contains resources in a ``.bsa`` file, go to near the top of the file, locate the entries like ''fallback-archive=Morrowind.bsa'' and create a new line underneath and type: ``fallback-archive=.bsa''``. .. note:: - Some text editors, such as TextEdit on Mac, will autocorrect your double quotes to typographical "curly" quotes instead of leaving them as the proper neutral vertical quotes ``""``. + Some text editors, such as TextEdit on Mac, will auto-correct your double quotes to typographical "curly" + quotes instead of leaving them as the proper neutral vertical quotes ``""``. #. Save your ``openmw.cfg`` file. -You have now installed your mod. Any simple replacer mods that only contain resource files such as meshes or textures will now automatically be loaded in the order of their ``data=*`` entry. This is important to note because replacer mods that replace the same resource will overwrite previous ones as you go down the list. +You have now installed your mod. Any simple replacer mods that only contain resource files such as meshes or +textures will now automatically be loaded in the order of their ``data=*`` entry. +This is important to note because replacer mods that replace the same resource will overwrite previous ones as you go down the list. Enable ------ -Any mods that have plugin files must be enabled to work. Master game files and plugin files can only be enabled if they have been properly installed within a *data folder* as described above. +Any mods that have plugin files must be enabled to work. +Master game files and plugin files can only be enabled if they have been properly installed within a *data folder* as described above. #. Open the OpenMW Launcher. #. Click on the Data Files tab. diff --git a/docs/source/reference/modding/settings/HUD.rst b/docs/source/reference/modding/settings/HUD.rst index 83811420f..07a3135c8 100644 --- a/docs/source/reference/modding/settings/HUD.rst +++ b/docs/source/reference/modding/settings/HUD.rst @@ -8,7 +8,8 @@ crosshair :Range: True/False :Default: True -This setting determines whether the crosshair or reticle is displayed. Enabling the crosshair provides more immediate feedback about which object is currently the focus of actions. +This setting determines whether the crosshair or reticle is displayed. +Enabling the crosshair provides more immediate feedback about which object is currently the focus of actions. Some players perceive that disabling the crosshair provides a more immersive experience. Another common use is to disable the crosshair for screen shots. diff --git a/docs/source/reference/modding/settings/cells.rst b/docs/source/reference/modding/settings/cells.rst index 43a984ca7..43e51eb7a 100644 --- a/docs/source/reference/modding/settings/cells.rst +++ b/docs/source/reference/modding/settings/cells.rst @@ -11,7 +11,10 @@ exterior cell load distance This setting determines the number of exterior cells adjacent to the character that will be loaded for rendering. .. Warning:: - Values greater than 1 will significantly affect the frame rate and loading times. This setting is mainly intended for making screenshots of scenic vistas and not for real-time gameplay. Loading more cells can break certain scripts or quests in the game that expect cells to not be loaded until the player is there. These limitations will be addressed in a future version with a separate technique for rendering distant cells. + Values greater than 1 will significantly affect the frame rate and loading times. + This setting is mainly intended for making screenshots of scenic vistas and not for real-time gameplay. + Loading more cells can break certain scripts or quests in the game that expect cells to not be loaded until the player is there. + These limitations will be addressed in a future version with a separate technique for rendering distant cells. This setting interacts with viewing distance and field of view settings. @@ -163,11 +166,13 @@ prediction time :Range: >=0 :Default: 1 -The amount of time (in seconds) in the future to predict the player position for. This predicted position is used to preload any cells and/or distant terrain required at that position. +The amount of time (in seconds) in the future to predict the player position for. +This predicted position is used to preload any cells and/or distant terrain required at that position. This setting will only have an effect if 'preload enabled' is set or the 'distant terrain' in the Terrain section is set. -Increasing this setting from its default may help if your computer/hard disk is too slow to preload in time and you see loading screens and/or lag spikes. +Increasing this setting from its default may help if your computer/hard disk is too slow to preload in time and you see +loading screens and/or lag spikes. cache expiry delay ------------------ @@ -185,7 +190,10 @@ target framerate :Range: >0 :Default: 60 -Affects the time to be set aside each frame for graphics preloading operations. The game will distribute the preloading over several frames so as to not go under the specified framerate. For best results, set this value to the monitor's refresh rate. If you still experience stutters on turning around, you can try a lower value, although the framerate during loading will suffer a bit in that case. +Affects the time to be set aside each frame for graphics preloading operations. +The game will distribute the preloading over several frames so as to not go under the specified framerate. +For best results, set this value to the monitor's refresh rate. If you still experience stutters on turning around, +you can try a lower value, although the framerate during loading will suffer a bit in that case. pointers cache size ------------------- @@ -194,4 +202,6 @@ pointers cache size :Range: >0 :Default: 40 -The count of object pointers that will be saved for a faster search by object ID. This is a temporary setting that can be used to mitigate scripting performance issues with certain game files. If your profiler (press F3 twice) displays a large overhead for the Scripting section, try increasing this setting. +The count of object pointers that will be saved for a faster search by object ID. +This is a temporary setting that can be used to mitigate scripting performance issues with certain game files. +If your profiler (press F3 twice) displays a large overhead for the Scripting section, try increasing this setting. diff --git a/docs/source/reference/modding/settings/game.rst b/docs/source/reference/modding/settings/game.rst index 4c3f4579f..475a2f175 100644 --- a/docs/source/reference/modding/settings/game.rst +++ b/docs/source/reference/modding/settings/game.rst @@ -9,11 +9,11 @@ show owned :Default: 0 Enable visual clues for items owned by NPCs when the crosshair is on the object. -If the setting is 0, no clues are provided which is the default Morrowind behavior. +If the setting is 0, no clues are provided which is the default Morrowind behaviour. If the setting is 1, the background of the tool tip for the object is highlighted -in the color specified by the color background owned setting in the GUI Settings Section. -If the setting is 2, the crosshair is the color of the color crosshair owned setting in the GUI Settings section. -If the setting is 3, both the tool tip background and the crosshair are colored. +in the colour specified by the colour background owned setting in the GUI Settings Section. +If the setting is 2, the crosshair is the colour of the colour crosshair owned setting in the GUI Settings section. +If the setting is 3, both the tool tip background and the crosshair are coloured. The crosshair is not visible if crosshair is false. This setting can be configured in Advanced tab of the launcher. @@ -71,12 +71,14 @@ can loot during death animation :Range: True/False :Default: True -If this setting is true, the player is allowed to loot actors (e.g. summoned creatures) during death animation, if they are not in combat. -However disposing corpses during death animation is not recommended - death counter may not be incremented, and this behaviour can break quests. +If this setting is true, the player is allowed to loot actors (e.g. summoned creatures) during death animation, +if they are not in combat. However disposing corpses during death animation is not recommended - +death counter may not be incremented, and this behaviour can break quests. This is how Morrowind behaves. If this setting is false, player has to wait until end of death animation in all cases. -This case is more safe, but makes using of summoned creatures exploit (looting summoned Dremoras and Golden Saints for expensive weapons) a lot harder. +This case is more safe, but makes using of summoned creatures exploit +(looting summoned Dremoras and Golden Saints for expensive weapons) a lot harder. Conflicts with mannequin mods, which use SkipAnim to prevent end of death animation. This setting can be toggled in Advanced tab of the launcher. @@ -167,7 +169,8 @@ use additional anim sources :Default: False Allow to load additional animation sources when enabled. -For example, if the main animation mesh has name Meshes/x.nif, an engine will load all KF-files from Animations/x folder and its child folders. +For example, if the main animation mesh has name Meshes/x.nif, +an engine will load all KF-files from Animations/x folder and its child folders. Can be useful if you want to use several animation replacers without merging them. Attention: animations from AnimKit have own format and are not supposed to be directly loaded in-game! This setting can only be configured by editing the settings configuration file. @@ -179,7 +182,8 @@ barter disposition change is permanent :Range: True/False :Default: False -If this setting is true, disposition change of merchants caused by trading will be permanent and won't be discarded upon exiting dialogue with them. +If this setting is true, +disposition change of merchants caused by trading will be permanent and won't be discarded upon exiting dialogue with them. This imitates the option Morrowind Code Patch offers. This setting can be toggled in Advanced tab of the launcher. diff --git a/docs/source/reference/modding/settings/map.rst b/docs/source/reference/modding/settings/map.rst index c973c26e5..2cf53f704 100644 --- a/docs/source/reference/modding/settings/map.rst +++ b/docs/source/reference/modding/settings/map.rst @@ -8,7 +8,8 @@ global :Range: True/False :Default: False -If this value is true, the map window will display the world map, otherwise the local map. The setting updates automatically when pressing the local/world map switch button on the map window. +If this value is true, the map window will display the world map, otherwise the local map. +The setting updates automatically when pressing the local/world map switch button on the map window. global map cell size -------------------- @@ -110,4 +111,6 @@ local map cell distance :Range: >= 1 :Default: 1 -Similar to "exterior cell load distance" in the Cells section, controls how many cells are rendered on the local map. Values higher than the default may result in longer loading times. Please note that only loaded cells can be rendered, so this setting must be lower or equal to "exterior cell load distance" to work properly. +Similar to "exterior cell load distance" in the Cells section, controls how many cells are rendered on the local map. +Values higher than the default may result in longer loading times. Please note that only loaded cells can be rendered, +so this setting must be lower or equal to "exterior cell load distance" to work properly. diff --git a/docs/source/reference/modding/settings/saves.rst b/docs/source/reference/modding/settings/saves.rst index 5add36c0c..0e58d66d8 100644 --- a/docs/source/reference/modding/settings/saves.rst +++ b/docs/source/reference/modding/settings/saves.rst @@ -29,7 +29,8 @@ timeplayed :Default: False This setting determines whether the amount of the time the player has spent playing will be displayed -for each saved game in the Load menu. Currently, the counter includes time spent in menus, including the pause menu, but does not include time spent with the game window minimized. +for each saved game in the Load menu. Currently, the counter includes time spent in menus, including the pause menu, +but does not include time spent with the game window minimized. This setting can only be configured by editing the settings configuration file. @@ -40,6 +41,8 @@ max quicksaves :Range: >0 :Default: 1 -This setting determines how many quicksave and autosave slots you can have at a time. If greater than 1, quicksaves will be sequentially created each time you quicksave. Once the maximum number of quicksaves has been reached, the oldest quicksave will be recycled the next time you perform a quicksave. +This setting determines how many quicksave and autosave slots you can have at a time. If greater than 1, +quicksaves will be sequentially created each time you quicksave. Once the maximum number of quicksaves has been reached, +the oldest quicksave will be recycled the next time you perform a quicksave. This setting can only be configured by editing the settings configuration file. diff --git a/docs/source/reference/modding/settings/shaders.rst b/docs/source/reference/modding/settings/shaders.rst index be1ecebf0..b36f64285 100644 --- a/docs/source/reference/modding/settings/shaders.rst +++ b/docs/source/reference/modding/settings/shaders.rst @@ -39,7 +39,8 @@ Only affects objects that render with shaders (see 'force shaders' option). Always affects terrain. Leaving this option at its default makes the lighting compatible with Morrowind's fixed-function method, -but the lighting may appear dull and there might be color shifts. Setting this option to 'false' results in more dynamic lighting. +but the lighting may appear dull and there might be colour shifts. +Setting this option to 'false' results in more dynamic lighting. auto use object normal maps --------------------------- @@ -83,7 +84,7 @@ auto use terrain specular maps :Default: False If a file with pattern 'terrain specular map pattern' exists, use that file as a 'diffuse specular' map. -The texture must contain the layer color in the RGB channel (as usual), and a specular multiplier in the alpha channel. +The texture must contain the layer colour in the RGB channel (as usual), and a specular multiplier in the alpha channel. normal map pattern ------------------ diff --git a/docs/source/reference/modding/settings/sound.rst b/docs/source/reference/modding/settings/sound.rst index b9587a5c4..895b919fb 100644 --- a/docs/source/reference/modding/settings/sound.rst +++ b/docs/source/reference/modding/settings/sound.rst @@ -67,7 +67,7 @@ voice volume :Range: 0.0 (silent) to 1.0 (maximum volume) :Default: 0.8 -This setting controls the volume for spoken dialog from NPCs. +This setting controls the volume for spoken dialogue from NPCs. This setting can be changed in game using the Voice slider from the Audio panel of the Options menu. @@ -122,7 +122,7 @@ hrtf This setting specifies which HRTF profile to use when HRTF is enabled. Blank means use the default. This setting has no effect if HRTF is not enabled based on the hrtf enable setting. -Allowed values for this field are enumerated in openmw.log file is an HRTF enabled ausio system is installed. +Allowed values for this field are enumerated in openmw.log file is an HRTF enabled audio system is installed. The default value is empty, which uses the default profile. This setting can only be configured by editing the settings configuration file. diff --git a/docs/source/reference/modding/settings/terrain.rst b/docs/source/reference/modding/settings/terrain.rst index 7f641888a..687f55e5e 100644 --- a/docs/source/reference/modding/settings/terrain.rst +++ b/docs/source/reference/modding/settings/terrain.rst @@ -12,7 +12,8 @@ Controls whether the engine will use paging and LOD algorithms to load the terra Otherwise, only the terrain of the surrounding cells is loaded. .. note:: - When enabling distant terrain, make sure the 'viewing distance' in the camera section is set to a larger value so that you can actually see the additional terrain. + When enabling distant terrain, make sure the 'viewing distance' in the camera section is set to a larger value so + that you can actually see the additional terrain. To avoid frame drops as the player moves around, nearby terrain pages are always preloaded in the background, regardless of the preloading settings in the 'Cells' section, diff --git a/docs/source/reference/modding/settings/video.rst b/docs/source/reference/modding/settings/video.rst index 3eb7c5128..a8a95739e 100644 --- a/docs/source/reference/modding/settings/video.rst +++ b/docs/source/reference/modding/settings/video.rst @@ -170,7 +170,8 @@ contrast This setting controls the contrast correction for all video in the game. -This setting can only be configured by editing the settings configuration file. It has been reported to not work on some Linux systems. +This setting can only be configured by editing the settings configuration file. +It has been reported to not work on some Linux systems. gamma ----- @@ -183,4 +184,5 @@ This setting controls the gamma correction for all video in the game. Gamma is an exponent that makes colors brighter if greater than 1.0 and darker if less than 1.0. This setting can be changed in the Detail tab of the Video panel of the Options menu. -It has been reported to not work on some Linux systems, and therefore the in-game setting in the Options menu has been disabled on Linux systems. +It has been reported to not work on some Linux systems, +and therefore the in-game setting in the Options menu has been disabled on Linux systems. diff --git a/docs/source/reference/modding/settings/windows.rst b/docs/source/reference/modding/settings/windows.rst index 2fae9c3e3..2a60745c2 100644 --- a/docs/source/reference/modding/settings/windows.rst +++ b/docs/source/reference/modding/settings/windows.rst @@ -25,7 +25,8 @@ Hand editing the configuration file might result in some fine tuning for alignme but the settings will be overwritten if a window is moved. .. note:: - To scale the windows, making the widgets proportionally larger, see the scaling factor setting in the GUI section instead. + To scale the windows, making the widgets proportionally larger, + see the scaling factor setting in the GUI section instead. :Type: boolean :Range: True/False @@ -210,7 +211,7 @@ dialogue w = 0.45 -The dialog window, for talking with NPCs. +The dialogue window, for talking with NPCs. Activated by clicking on a NPC. alchemy diff --git a/docs/source/reference/modding/texture-modding/convert-bump-mapped-mods.rst b/docs/source/reference/modding/texture-modding/convert-bump-mapped-mods.rst index 421fa67a7..5fbff38d4 100644 --- a/docs/source/reference/modding/texture-modding/convert-bump-mapped-mods.rst +++ b/docs/source/reference/modding/texture-modding/convert-bump-mapped-mods.rst @@ -20,29 +20,43 @@ General introduction to normal map conversion :Authors: Joakim (Lysol) Berg :Updated: 2016-11-11 -This page has general information and tutorials on how normal mapping works in OpenMW and how you can make mods using the old fake normal mapping technique (such as `Netch Bump mapped`_ and `Hlaalu Bump mapped`_, and maybe the most (in)famous one to give shiny rocks in OpenMW, the mod `On the Rocks`_!, featured in MGSO and Morrowind Rebirth) work in OpenMW. +This page has general information and tutorials on how normal mapping works in OpenMW and how you can make mods using +the old fake normal mapping technique (such as `Netch Bump mapped`_ and `Hlaalu Bump mapped`_, and maybe the most +(in)famous one to give shiny rocks in OpenMW, the mod `On the Rocks`_!, featured in MGSO and Morrowind Rebirth) work in OpenMW. *Note:* The conversion made in the `Converting Apel's Various Things - Sacks`_-part of this tutorial require the use of the application NifSkope_. -*Another note:* I will use the terms bump mapping and normal mapping simultaneously. Normal mapping is one form of bump mapping. In other words, normal mapping is bump mapping, but bump mapping isn't necessarily normal mapping. There are several techniques for bump mapping, and normal mapping is the most common one today. +*Another note:* I will use the terms bump mapping and normal mapping simultaneously. +Normal mapping is one form of bump mapping. In other words, normal mapping is bump mapping, +but bump mapping isn't necessarily normal mapping. +There are several techniques for bump mapping, and normal mapping is the most common one today. So let's get on with it. Normal Mapping in OpenMW ************************ -Normal mapping in OpenMW works in a very simple way: The engine just looks for a texture with a *_n.dds* suffix, and you're done. +Normal mapping in OpenMW works in a very simple way: The engine just looks for a texture with a *_n.dds* suffix, +and you're done. So to expand on this a bit, let's take a look at how a model seeks for textures. -Let us assume we have the model *example.nif*. In this model file, there should be a tag (NiSourceTexture) that states what texture it should use and where to find it. Typically, it will point to something like *exampletexture_01.dds*. This texture is supposed to be located directly in the Textures folder since it does not state anything else. If the model is a custom made one, modders tend to group their textures in separate folders, just to easily keep track of them. It might be something like *./Textures/moddername/exampletexture_02.dds*. +Let us assume we have the model *example.nif*. In this model file, +there should be a tag (NiSourceTexture) that states what texture it should use and where to find it. Typically, +it will point to something like *exampletexture_01.dds*. This texture is supposed to be located directly in the +Textures folder since it does not state anything else. If the model is a custom made one, modders tend to group +their textures in separate folders, just to easily keep track of them. +It might be something like *./Textures/moddername/exampletexture_02.dds*. -When OpenMW finally adds normal mapping, it simply takes the NiSourceTexture file path, e.g., *exampletexture_01.dds*, and looks for a *exampletexture_01_n.dds*. If it can't find this file, no normal mapping is added. If it *does* find this file, the model will use this texture as a normal map. Simple. +When OpenMW finally adds normal mapping, it simply takes the NiSourceTexture file path, e.g., +*exampletexture_01.dds*, and looks for a *exampletexture_01_n.dds*. If it can't find this file, no normal mapping is added. +If it *does* find this file, the model will use this texture as a normal map. Simple. Activating normal mapping shaders in OpenMW ******************************************* -Before normal (and specular and parallax) maps will show up in OpenMW, you'll need to activate them in the settings.cfg_-file. Add these rows where it would make sense: +Before normal (and specular and parallax) maps will show up in OpenMW, you'll need to activate them in the +settings.cfg_-file. Add these rows where it would make sense: :: @@ -72,13 +86,26 @@ Normal mapping in Morrowind with Morrowind Code Patch **Conversion difficulty:** *Varies. Sometimes quick and easy, sometimes time-consuming and hard.* -You might have bumped (pun intended) on a few bump-mapped texture packs for Morrowind that require the Morrowind Code Patch (MCP). You might even be thinking: Why doesn't OpenMW just support these instead of reinventing the wheel? I know it sounds strange, but it will make sense. Here's how MCP handles normal maps: +You might have bumped (pun intended) on a few bump-mapped texture packs for Morrowind that require the +Morrowind Code Patch (MCP). You might even be thinking: Why doesn't OpenMW just support these instead of reinventing +the wheel? I know it sounds strange, but it will make sense. Here's how MCP handles normal maps: -Morrowind does not recognize normal maps (they weren't really a "thing" yet in 2002), so even if you have a normal map, Morrowind will not load and display it. MCP has a clever way to solve this issue, by using something Morrowind *does* support, namely environment maps. You could add a tag for an environment map and then add a normal map as the environment map, but you'd end up with a shiny ugly model in the game. MCP solves this by turning down the brightness of the environment maps, making the model look *kind of* as if it had a normal map applied to it. I say kind of because it does not really look as good as normal mapping usually does. It was a hacky way to do it, but it was the only way at the time, and therefore the best way. +Morrowind does not recognize normal maps (they weren't really a "thing" yet in 2002), so even if you have a normal map, +Morrowind will not load and display it. MCP has a clever way to solve this issue, by using something Morrowind *does* support, +namely environment maps. You could add a tag for an environment map and then add a normal map as the environment map, +but you'd end up with a shiny ugly model in the game. MCP solves this by turning down the brightness of the environment maps, +making the model look *kind of* as if it had a normal map applied to it. +I say kind of because it does not really look as good as normal mapping usually does. It was a hacky way to do it, +but it was the only way at the time, and therefore the best way. -The biggest problem with this is not that it doesn't look as good as it could – no, the biggest problem in my opinion is that it requires you to state the file paths for your normal map textures *in the models*! For buildings, which often use several textures for one single model file, it could take *ages* to do this, and you had to do it for dozens of model files too. You also had to ship your texture pack with model files, making your mod bigger in file size. +The biggest problem with this is not that it doesn't look as good as it could – no, +the biggest problem in my opinion is that it requires you to state the file paths for your normal map textures *in the models*! +For buildings, which often use several textures for one single model file, it could take *ages* to do this, +and you had to do it for dozens of model files too. You also had to ship your texture pack with model files, +making your mod bigger in file size. -These are basically the reasons why OpenMW does not support fake bump maps like MCP does. It is just a really bad way to enhance your models, all the more when you have the possibility to do it in a better way. +These are basically the reasons why OpenMW does not support fake bump maps like MCP does. +It is just a really bad way to enhance your models, all the more when you have the possibility to do it in a better way. Normal mapping in Morrowind with MGE XE *************************************** @@ -86,9 +113,18 @@ Normal mapping in Morrowind with MGE XE **Conversion difficulty:** *Easy* -The most recent feature on this topic is that the Morrowind Graphics Extender (MGE) finally started to support real normal mapping in an experimental version available here: `MGE XE`_ (you can't use MGE with OpenMW!). Not only this but it also adds full support for physically based rendering (PBR), making it one step ahead of OpenMW in terms of texturing techniques. However, OpenMW will probably have this feature in the future too – and let's hope that OpenMW and MGE will handle PBR in a similar fashion in the future so that mods can be used for both MGE and OpenMW without any hassle. +The most recent feature on this topic is that the Morrowind Graphics Extender (MGE) finally started to support real +normal mapping in an experimental version available here: `MGE XE`_ (you can't use MGE with OpenMW!). +Not only this but it also adds full support for physically based rendering (PBR), +making it one step ahead of OpenMW in terms of texturing techniques. However, +OpenMW will probably have this feature in the future too – and let's hope that OpenMW and MGE will handle PBR in a +similar fashion in the future so that mods can be used for both MGE and OpenMW without any hassle. -I haven't researched that much on the MGE variant yet but it does support real implementation of normal mapping, making it really easy to convert mods made for MGE into OpenMW (I'm only talking about the normal map textures though). There's some kind of text file if I understood it correctly that MGE uses to find the normal map. OpenMW does not need this, you just have to make sure the normal map has the same name as the diffuse texture but with the correct suffix after. +I haven't researched that much on the MGE variant yet but it does support real implementation of normal mapping, +making it really easy to convert mods made for MGE into OpenMW (I'm only talking about the normal map textures though). +There's some kind of text file if I understood it correctly that MGE uses to find the normal map. +OpenMW does not need this, you just have to make sure the normal map has the same name as the diffuse texture but with +the correct suffix after. Now, on to the tutorials. @@ -99,14 +135,21 @@ Converting PeterBitt's Scamp Replacer :Authors: Joakim (Lysol) Berg :Updated: 2016-11-11 -So, let's say you've found out that PeterBitt_ makes awesome models and textures featuring physically based rendering (PBR) and normal maps. Let's say that you tried to run his `PBR Scamp Replacer`_ in OpenMW and that you were greatly disappointed when the normal map didn't seem to work. Lastly, let's say you came here, looking for some answers. Am I right? Great. Because you've come to the right place! +So, let's say you've found out that PeterBitt_ makes awesome models and textures featuring physically based rendering +(PBR) and normal maps. Let's say that you tried to run his `PBR Scamp Replacer`_ in OpenMW and that you were greatly +disappointed when the normal map didn't seem to work. Lastly, let's say you came here, looking for some answers. +Am I right? Great. Because you've come to the right place! -*A quick note before we begin*: Please note that you can only use the normal map texture and not the rest of the materials, since PBR isn't implemented in OpenMW yet. Sometimes PBR textures can look dull without all of the texture files, so have that in mind. +*A quick note before we begin*: Please note that you can only use the normal map texture and not the rest of the materials, +since PBR isn't implemented in OpenMW yet. Sometimes PBR textures can look dull without all of the texture files, +so have that in mind. Tutorial - MGE ************** -In this tutorial, I will use PeterBitt's `PBR Scamp Replacer`_ as an example, but any mod featuring PBR that requires the PBR version of MGE will do, provided it also includes a normal map (which it probably does). +In this tutorial, I will use PeterBitt's `PBR Scamp Replacer`_ as an example, +but any mod featuring PBR that requires the PBR version of MGE will do, +provided it also includes a normal map (which it probably does). So, follow these steps: @@ -120,11 +163,14 @@ So, follow these steps: #. Rename your newly extracted file (``tx_Scamp_normals.dds``) to ``tx_Scamp_n.dds`` (which is exactly the same name as the diffuse texture file, except for the added *_n* suffix before the filename extention). #. You're actually done! -So as you might notice, converting these mods is very simple and takes just a couple of minutes. It's more or less just a matter of renaming and moving a few files. +So as you might notice, converting these mods is very simple and takes just a couple of minutes. +It's more or less just a matter of renaming and moving a few files. -I totally recommend you to also try this on PeterBitt's Nix Hound replacer and Flash3113's various replacers. It should be the same principle to get those to work. +I totally recommend you to also try this on PeterBitt's Nix Hound replacer and Flash3113's various replacers. +It should be the same principle to get those to work. -And let's hope that some one implements PBR shaders to OpenMW too, so that we can use all the material files of these mods in the future. +And let's hope that some one implements PBR shaders to OpenMW too, +so that we can use all the material files of these mods in the future. Converting Lougian's Hlaalu Bump mapped --------------------------------------- @@ -133,12 +179,17 @@ Converting Lougian's Hlaalu Bump mapped :Authors: Joakim (Lysol) Berg :Updated: 2016-11-11 -Converting textures made for the Morrowind Code Patch (MCP) fake bump mapping can be really easy or a real pain, depending on a few circumstances. In this tutorial, we will look at a very easy, although in some cases a bit time-consuming, example. +Converting textures made for the Morrowind Code Patch (MCP) fake bump mapping can be really easy or a real pain, +depending on a few circumstances. In this tutorial, we will look at a very easy, +although in some cases a bit time-consuming, example. Tutorial - MCP, Part 1 ********************** -We will be converting a quite popular texture replacer of the Hlaalu architecture, namely Lougian's `Hlaalu Bump mapped`_. Since this is just a texture pack and not a model replacer, we can convert the mod in a few minutes by just renaming a few dozen files and by *not* extracting the included model (``.nif``) files when installing the mod. +We will be converting a quite popular texture replacer of the Hlaalu architecture, namely Lougian's `Hlaalu Bump mapped`_. +Since this is just a texture pack and not a model replacer, +we can convert the mod in a few minutes by just renaming a few dozen files and by *not* extracting the included model +(``.nif``) files when installing the mod. #. Download Lougian's `Hlaalu Bump mapped`_. #. Install the mod by extracting the ``./Textures`` folder to a data folder the way you usually install mods (**Pro tip**: Install using OpenMW's `Multiple data folders`_ function!). @@ -149,7 +200,8 @@ We will be converting a quite popular texture replacer of the Hlaalu architectur - As a nice bonus to this tutorial, this pack actually included one specularity texture too. We should use it of course. It's the one called "``tx_glass_amber_02_reflection.dds``". For OpenMW to recognize this file and use it as a specular map, you need to change the *_reflection.dds* part to *_spec.dds*, resulting in the name ``tx_glass_amber_01_spec.dds``. #. That should be it. Really simple, but I do know that it takes a few minutes to rename all those files. -Now – if the mod you want to change includes custom made models it gets a bit more complicated I'm afraid. But that is for the next tutorial. +Now – if the mod you want to change includes custom made models it gets a bit more complicated I'm afraid. +But that is for the next tutorial. Converting Apel's Various Things - Sacks ---------------------------------------- @@ -158,12 +210,16 @@ Converting Apel's Various Things - Sacks :Authors: Joakim (Lysol) Berg :Updated: 2016-11-09 -In part one of this tutorial, we converted a mod that only included modified Morrowind model (``.nif``) files so that the normal maps could be loaded in Morrowind with MCP. We ignored those model files since they are not needed with OpenMW. In this tutorial however, we will convert a mod that includes new, custom made models. In other words, we cannot just ignore those files this time. +In part one of this tutorial, we converted a mod that only included modified Morrowind model (``.nif``) +files so that the normal maps could be loaded in Morrowind with MCP. +We ignored those model files since they are not needed with OpenMW. In this tutorial however, +we will convert a mod that includes new, custom made models. In other words, we cannot just ignore those files this time. Tutorial - MCP, Part 2 ********************** -The sacks included in Apel's `Various Things - Sacks`_ come in two versions – Without bump mapping, and with bump mapping. Since we want the glory of normal mapping in our OpenMW setup, we will go with the bump-mapped version. +The sacks included in Apel's `Various Things - Sacks`_ come in two versions – Without bump mapping, and with bump mapping. +Since we want the glory of normal mapping in our OpenMW setup, we will go with the bump-mapped version. #. Start by downloading Apel's `Various Things - Sacks`_ from Nexus. #. Once downloaded, install it the way you'd normally install your mods (**Pro tip**: Install using OpenMW's `Multiple data folders`_ function!). @@ -181,7 +237,9 @@ The sacks included in Apel's `Various Things - Sacks`_ come in two versions – #. OpenMW detects normal maps if they have the same name as the base diffuse texture, but with a *_n.dds* suffix. In this mod, the normal maps has a suffix of *_nm.dds*. Change all the files that ends with *_nm.dds* to instead end with *_n.dds*. #. Finally, `we are done`_! -Since these models have one or two textures applied to them, the fix was not that time-consuming. It gets worse when you have to fix a model that uses loads of textures. The principle is the same, it just requires more manual work which is annoying and takes time. +Since these models have one or two textures applied to them, the fix was not that time-consuming. +It gets worse when you have to fix a model that uses loads of textures. The principle is the same, +it just requires more manual work which is annoying and takes time. .. _`Netch Bump mapped`: https://www.nexusmods.com/morrowind/mods/42851/? .. _`Hlaalu Bump mapped`: https://www.nexusmods.com/morrowind/mods/42396/? diff --git a/docs/source/reference/modding/texture-modding/texture-basics.rst b/docs/source/reference/modding/texture-modding/texture-basics.rst index d9137c89d..51523af3f 100644 --- a/docs/source/reference/modding/texture-modding/texture-basics.rst +++ b/docs/source/reference/modding/texture-modding/texture-basics.rst @@ -19,10 +19,16 @@ OpenMW automatically uses shaders for objects with these mapping techniques. Normal Mapping ############## -To plug in a normal map, you name the normal map as the diffuse texture but with a specified suffix after. OpenMW will then recognise the file and load it as a normal map, provided you have set up your settings file correctly. See the section `Automatic use`_ further down below for detailed information. +To plug in a normal map, you name the normal map as the diffuse texture but with a specified suffix after. +OpenMW will then recognise the file and load it as a normal map, provided you have set up your settings file correctly. +See the section `Automatic use`_ further down below for detailed information. .. note:: - While the original Morrowind engine does support the loading of a BumpTexture slot in the NIF, it will not display it as a normal map. Morrowind Code Patch (MCP) added a way to hack normal maps into the engine by first enabling the engine to load the BumpTexture slot as an environment map and then turn down the brightness of the environment map. This will imitate how a real normal map shader would display a normal map, but it will not look exactly the same. + While the original Morrowind engine does support the loading of a BumpTexture slot in the NIF, + it will not display it as a normal map. Morrowind Code Patch (MCP) + added a way to hack normal maps into the engine by first enabling the engine to load the BumpTexture slot as an + environment map and then turn down the brightness of the environment map. + This will imitate how a real normal map shader would display a normal map, but it will not look exactly the same. OpenMW uses standard normal mapping, which achieves much better results. Unfortunately, this difference can result in incompatibilities. Some mods From b6f23cd3661b7195f1068a4aff162e7518130beb Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 27 Oct 2018 13:27:25 +0300 Subject: [PATCH 148/163] Make constants usage more obvious --- apps/openmw/mwrender/animation.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 8ae8800a6..8463b7bfe 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -15,13 +15,13 @@ #include -#include - #include #include #include #include +#include + #include // KeyframeHolder #include @@ -1791,10 +1791,10 @@ namespace MWRender { // TODO: use global attenuation settings - // 1 pt of Light effect magnitude corresponds to 1 foot of light source radius, which is about 21.33 game units, - // but Morrowind uses imprecise value of foot for magic effects. - float radius = effect * 22.f; - float linearAttenuation = 3.f / radius; + // 1 pt of Light magnitude corresponds to 1 foot of radius + float radius = effect * std::ceil(Constants::UnitsPerFoot); + const float linearValue = 3.f; // Currently hardcoded: unmodified Morrowind attenuation settings + float linearAttenuation = linearValue / radius; if (!mGlowLight || linearAttenuation != mGlowLight->getLight(0)->getLinearAttenuation()) { From abdf40e0d54bfc4d8970a5ca742ad998586ce355 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 27 Oct 2018 16:33:26 +0300 Subject: [PATCH 149/163] Avoid making expensive visitEffectSources calls if no spell absorption effect is active --- apps/openmw/mwmechanics/spellcasting.cpp | 39 +++++++++++++----------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 4a74e1647..7c9e207e9 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -472,28 +472,31 @@ namespace MWMechanics MWBase::Environment::get().getWindowManager()->setEnemy(target); // Try absorbing if it's a spell - // NOTE: Vanilla does this once per spell absorption effect source instead of adding the % from all sources together, so use the same approach here + // Unlike Reflect, this is done once per spell absorption effect source bool absorbed = false; if (spell && caster != target && target.getClass().isActor()) { - GetAbsorptionProbability check(target); - MWMechanics::CreatureStats& stats = target.getClass().getCreatureStats(target); - stats.getActiveSpells().visitEffectSources(check); - stats.getSpells().visitEffectSources(check); - if (target.getClass().hasInventoryStore(target)) - target.getClass().getInventoryStore(target).visitEffectSources(check); - - int absorb = check.mProbability * 100; - absorbed = (Misc::Rng::roll0to99() < absorb); - if (absorbed) + CreatureStats& stats = target.getClass().getCreatureStats(target); + if (stats.getMagicEffects().get(ESM::MagicEffect::SpellAbsorption).getMagnitude() > 0.f) { - const ESM::Static* absorbStatic = MWBase::Environment::get().getWorld()->getStore().get().find ("VFX_Absorb"); - MWBase::Environment::get().getWorld()->getAnimation(target)->addEffect( - "meshes\\" + absorbStatic->mModel, ESM::MagicEffect::SpellAbsorption, false, ""); - // Magicka is increased by cost of spell - DynamicStat magicka = target.getClass().getCreatureStats(target).getMagicka(); - magicka.setCurrent(magicka.getCurrent() + spell->mData.mCost); - target.getClass().getCreatureStats(target).setMagicka(magicka); + GetAbsorptionProbability check(target); + stats.getActiveSpells().visitEffectSources(check); + stats.getSpells().visitEffectSources(check); + if (target.getClass().hasInventoryStore(target)) + target.getClass().getInventoryStore(target).visitEffectSources(check); + + int absorb = check.mProbability * 100; + absorbed = (Misc::Rng::roll0to99() < absorb); + if (absorbed) + { + const ESM::Static* absorbStatic = MWBase::Environment::get().getWorld()->getStore().get().find ("VFX_Absorb"); + MWBase::Environment::get().getWorld()->getAnimation(target)->addEffect( + "meshes\\" + absorbStatic->mModel, ESM::MagicEffect::SpellAbsorption, false, ""); + // Magicka is increased by cost of spell + DynamicStat magicka = stats.getMagicka(); + magicka.setCurrent(magicka.getCurrent() + spell->mData.mCost); + stats.setMagicka(magicka); + } } } From 7a7b47b0ba6a6582ac9ac501f31b2a67bc83c7ea Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 27 Oct 2018 20:31:03 +0400 Subject: [PATCH 150/163] Add missing changelog entries --- CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ed387eaf6..4d49672e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ Bug #2256: Landing sound not playing when jumping immediately after landing Bug #2274: Thin platform clips through player character instead of lifting Bug #2326: After a bound item expires the last equipped item of that type is not automatically re-equipped + Bug #2446: Restore Attribute/Skill should allow restoring drained attributes Bug #2455: Creatures attacks degrade armor Bug #2562: Forcing AI to activate a teleport door sometimes causes a crash Bug #2626: Resurrecting the player does not resume the game @@ -46,6 +47,7 @@ Bug #4110: Fixed undo / redo menu text losing the assigned shortcuts Bug #4125: OpenMW logo cropped on bugtracker Bug #4215: OpenMW shows book text after last EOL tag + Bug #4217: Fixme implementation differs from Morrowind's 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 @@ -55,6 +57,7 @@ Bug #4274: Pre-0.43 death animations are not forward-compatible with 0.43+ Bug #4286: Scripted animations can be interrupted Bug #4291: Non-persistent actors that started the game as dead do not play death animations + Bug #4292: CenterOnCell implementation differs from vanilla 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 @@ -65,7 +68,7 @@ Bug #4368: Settings window ok button doesn't have key focus by default Bug #4378: On-self absorb spells restore stats Bug #4393: NPCs walk back to where they were after using ResetActors - Bug #4416: Handle exception if we try to play non-music file + Bug #4416: Non-music files crash the game when they are tried to be played Bug #4419: MRK NiStringExtraData is handled incorrectly Bug #4426: RotateWorld behavior is incorrect Bug #4429: [Windows] Error on build INSTALL.vcxproj project (debug) with cmake 3.7.2 @@ -81,6 +84,7 @@ Bug #4459: NotCell dialogue condition doesn't support partial matches Bug #4460: Script function "Equip" doesn't bypass beast restrictions Bug #4461: "Open" spell from non-player caster isn't a crime + Bug #4463: %g format doesn't return more digits Bug #4464: OpenMW keeps AiState cached storages even after we cancel AI packages Bug #4467: Content selector: cyrillic characters are decoded incorrectly in plugin descriptions Bug #4469: Abot Silt Striders – Model turn 90 degrees on horizontal From d27d48eb2259338bb1296bb67cacd0a53d839aa5 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 27 Oct 2018 19:45:40 +0300 Subject: [PATCH 151/163] Fix revert record icon --- files/opencs/resources.qrc | 1 + 1 file changed, 1 insertion(+) diff --git a/files/opencs/resources.qrc b/files/opencs/resources.qrc index 93b47dbdd..4fc4bdf28 100644 --- a/files/opencs/resources.qrc +++ b/files/opencs/resources.qrc @@ -94,6 +94,7 @@ record-preview.png record-clone.png record-add.png + record-revert.png resources-icon.png resources-mesh.png resources-music.png From 8337423e6c273ed507c5178cc35c1065766bdc26 Mon Sep 17 00:00:00 2001 From: Alec Nunn Date: Sat, 27 Oct 2018 20:16:51 -0700 Subject: [PATCH 152/163] Fixed up grammar in the game settings documentation. --- docs/source/reference/modding/settings/game.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/source/reference/modding/settings/game.rst b/docs/source/reference/modding/settings/game.rst index 475a2f175..124b54f38 100644 --- a/docs/source/reference/modding/settings/game.rst +++ b/docs/source/reference/modding/settings/game.rst @@ -168,11 +168,11 @@ use additional anim sources :Range: True/False :Default: False -Allow to load additional animation sources when enabled. +Allow the engine to load additional animation sources when enabled. For example, if the main animation mesh has name Meshes/x.nif, -an engine will load all KF-files from Animations/x folder and its child folders. -Can be useful if you want to use several animation replacers without merging them. -Attention: animations from AnimKit have own format and are not supposed to be directly loaded in-game! +the engine will load all KF-files from Animations/x folder and its child folders. +This can be useful if you want to use several animation replacers without merging them. +Attention: animations from AnimKit have their own format and are not supposed to be directly loaded in-game! This setting can only be configured by editing the settings configuration file. barter disposition change is permanent @@ -184,6 +184,6 @@ barter disposition change is permanent If this setting is true, disposition change of merchants caused by trading will be permanent and won't be discarded upon exiting dialogue with them. -This imitates the option Morrowind Code Patch offers. +This imitates the option that Morrowind Code Patch offers. This setting can be toggled in Advanced tab of the launcher. From 6e05853478585f5eb2034771f1246dbc310c9de5 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sun, 28 Oct 2018 11:44:14 +0400 Subject: [PATCH 153/163] Center progress bar when there are active messageboxes (bug #4691) --- CHANGELOG.md | 1 + apps/openmw/mwbase/windowmanager.hpp | 2 ++ apps/openmw/mwgui/loadingscreen.cpp | 8 ++++++-- apps/openmw/mwgui/loadingscreen.hpp | 2 +- apps/openmw/mwgui/messagebox.cpp | 5 +++++ apps/openmw/mwgui/messagebox.hpp | 2 ++ apps/openmw/mwgui/windowmanagerimp.cpp | 9 +++++++++ apps/openmw/mwgui/windowmanagerimp.hpp | 2 ++ apps/openmw/mwstate/statemanagerimp.cpp | 6 ++++-- apps/openmw/mwworld/scene.cpp | 6 ++++-- components/loadinglistener/loadinglistener.hpp | 2 +- 11 files changed, 37 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ed387eaf6..2200bccb1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -147,6 +147,7 @@ Bug #4684: Spell Absorption is additive Bug #4685: Missing sound causes an exception inside Say command Bug #4689: Default creature soundgen entries are not used + Bug #4691: Loading bar for cell should be moved up when text is still active at bottom of screen Feature #912: Editor: Add missing icons to UniversalId tables Feature #1221: Editor: Creature/NPC rendering Feature #1617: Editor: Enchantment effect record verifier diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 36beb25fc..2eec78526 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -285,6 +285,8 @@ namespace MWBase virtual void setEnemy (const MWWorld::Ptr& enemy) = 0; + virtual int getMessagesCount() const = 0; + virtual const Translation::Storage& getTranslationDataStorage() const = 0; /// Warning: do not use MyGUI::InputManager::setKeyFocusWidget directly. Instead use this. diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp index 3bb3ee260..7f641d408 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -96,7 +96,7 @@ namespace MWGui Log(Debug::Warning) << "Warning: no splash screens found!"; } - void LoadingScreen::setLabel(const std::string &label, bool important) + void LoadingScreen::setLabel(const std::string &label, bool important, bool center) { mImportantLabel = important; @@ -105,7 +105,11 @@ namespace MWGui MyGUI::IntSize size(mLoadingText->getTextSize().width+padding, mLoadingBox->getHeight()); size.width = std::max(300, size.width); mLoadingBox->setSize(size); - mLoadingBox->setPosition(mMainWidget->getWidth()/2 - mLoadingBox->getWidth()/2, mLoadingBox->getTop()); + + if (center) + mLoadingBox->setPosition(mMainWidget->getWidth()/2 - mLoadingBox->getWidth()/2, mMainWidget->getHeight()/2 - mLoadingBox->getHeight()/2); + else + mLoadingBox->setPosition(mMainWidget->getWidth()/2 - mLoadingBox->getWidth()/2, mMainWidget->getHeight() - mLoadingBox->getHeight() - 8); } void LoadingScreen::setVisible(bool visible) diff --git a/apps/openmw/mwgui/loadingscreen.hpp b/apps/openmw/mwgui/loadingscreen.hpp index bdd210d00..e74a60206 100644 --- a/apps/openmw/mwgui/loadingscreen.hpp +++ b/apps/openmw/mwgui/loadingscreen.hpp @@ -34,7 +34,7 @@ namespace MWGui virtual ~LoadingScreen(); /// Overridden from Loading::Listener, see the Loading::Listener documentation for usage details - virtual void setLabel (const std::string& label, bool important); + virtual void setLabel (const std::string& label, bool important, bool center); virtual void loadingOn(bool visible=true); virtual void loadingOff(); virtual void setProgressRange (size_t range); diff --git a/apps/openmw/mwgui/messagebox.cpp b/apps/openmw/mwgui/messagebox.cpp index e83c4b238..6c2d85dd9 100644 --- a/apps/openmw/mwgui/messagebox.cpp +++ b/apps/openmw/mwgui/messagebox.cpp @@ -35,6 +35,11 @@ namespace MWGui } } + int MessageBoxManager::getMessagesCount() + { + return mMessageBoxes.size(); + } + void MessageBoxManager::clear() { if (mInterMessageBoxe) diff --git a/apps/openmw/mwgui/messagebox.hpp b/apps/openmw/mwgui/messagebox.hpp index 156a17e67..e4e4b743c 100644 --- a/apps/openmw/mwgui/messagebox.hpp +++ b/apps/openmw/mwgui/messagebox.hpp @@ -28,6 +28,8 @@ namespace MWGui bool createInteractiveMessageBox (const std::string& message, const std::vector& buttons); bool isInteractiveMessageBox (); + int getMessagesCount(); + const InteractiveMessageBox* getInteractiveMessageBox() const { return mInterMessageBoxe; } /// Remove all message boxes diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 393751855..f02314de6 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -1670,6 +1670,15 @@ namespace MWGui mHud->setEnemy(enemy); } + int WindowManager::getMessagesCount() const + { + int count = 0; + if (mMessageBoxManager) + count = mMessageBoxManager->getMessagesCount(); + + return count; + } + Loading::Listener* WindowManager::getLoadingScreen() { return mLoadingScreen; diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index dc3cc4b09..050f1667d 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -316,6 +316,8 @@ namespace MWGui virtual void setEnemy (const MWWorld::Ptr& enemy); + virtual int getMessagesCount() const; + virtual const Translation::Storage& getTranslationDataStorage() const; void onSoulgemDialogButtonPressed (int button); diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 9804531d7..946c5eef7 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -265,9 +265,10 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot writer.save (stream); Loading::Listener& listener = *MWBase::Environment::get().getWindowManager()->getLoadingScreen(); + int messagesCount = MWBase::Environment::get().getWindowManager()->getMessagesCount(); // Using only Cells for progress information, since they typically have the largest records by far listener.setProgressRange(MWBase::Environment::get().getWorld()->countSavedGameCells()); - listener.setLabel("#{sNotifyMessage4}", true); + listener.setLabel("#{sNotifyMessage4}", true, messagesCount > 0); Loading::ScopedLoad load(&listener); @@ -389,9 +390,10 @@ void MWState::StateManager::loadGame (const Character *character, const std::str std::map contentFileMap = buildContentFileIndexMap (reader); Loading::Listener& listener = *MWBase::Environment::get().getWindowManager()->getLoadingScreen(); + int messagesCount = MWBase::Environment::get().getWindowManager()->getMessagesCount(); listener.setProgressRange(100); - listener.setLabel("#{sLoadingMessage14}"); + listener.setLabel("#{sLoadingMessage14}", false, messagesCount > 0); Loading::ScopedLoad load(&listener); diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 30e73df58..4c6cf1962 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -360,8 +360,9 @@ namespace MWWorld Loading::Listener* loadingListener = MWBase::Environment::get().getWindowManager()->getLoadingScreen(); Loading::ScopedLoad load(loadingListener); + int messagesCount = MWBase::Environment::get().getWindowManager()->getMessagesCount(); std::string loadingExteriorText = "#{sLoadingMessage3}"; - loadingListener->setLabel(loadingExteriorText); + loadingListener->setLabel(loadingExteriorText, false, messagesCount > 0); CellStoreCollection::iterator active = mActiveCells.begin(); while (active!=mActiveCells.end()) @@ -526,8 +527,9 @@ namespace MWWorld MWBase::Environment::get().getWindowManager()->fadeScreenOut(0.5); Loading::Listener* loadingListener = MWBase::Environment::get().getWindowManager()->getLoadingScreen(); + int messagesCount = MWBase::Environment::get().getWindowManager()->getMessagesCount(); std::string loadingInteriorText = "#{sLoadingMessage2}"; - loadingListener->setLabel(loadingInteriorText); + loadingListener->setLabel(loadingInteriorText, false, messagesCount > 0); Loading::ScopedLoad load(loadingListener); if(!loadcell) diff --git a/components/loadinglistener/loadinglistener.hpp b/components/loadinglistener/loadinglistener.hpp index 6c7a3b090..f5cfa5cf3 100644 --- a/components/loadinglistener/loadinglistener.hpp +++ b/components/loadinglistener/loadinglistener.hpp @@ -14,7 +14,7 @@ namespace Loading /// @note "non-important" labels may not show on screen if the loading process went so fast /// that the implementation decided not to show a loading screen at all. "important" labels /// will show in a separate message-box if the loading screen was not shown. - virtual void setLabel (const std::string& label, bool important=false) {} + virtual void setLabel (const std::string& label, bool important=false, bool center=false) {} /// Start a loading sequence. Must call loadingOff() when done. /// @note To get the loading screen to actually update, you must call setProgress / increaseProgress periodically. From 06d226a1b72841b97f73ad35c777ee856957aa98 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sun, 28 Oct 2018 15:08:24 +0400 Subject: [PATCH 154/163] Minor tweaks for actors processing range setting --- CHANGELOG.md | 1 + apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 5 +++++ files/mygui/openmw_settings_window.layout | 6 +++--- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b707851de..3ee1823d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -178,6 +178,7 @@ Feature #4632: AI priority: utilize vanilla AI GMSTs for priority rating Feature #4636: Use sTo GMST in spellmaking menu Feature #4642: Batching potion creation + Feature #4647: Cull actors outside of AI processing range Feature #4682: Use the collision box from basic creature mesh if the X one have no collisions Task #2490: Don't open command prompt window on Release-mode builds automatically Task #4545: Enable is_pod string test diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 1c7576316..3c540a9d4 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -17,6 +17,7 @@ #include "../mwworld/ptr.hpp" #include "../mwbase/environment.hpp" +#include "../mwbase/statemanager.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/dialoguemanager.hpp" @@ -437,6 +438,10 @@ namespace MWMechanics { if (it->first == "Game" && it->second == "actors processing range") { + int state = MWBase::Environment::get().getStateManager()->getState(); + if (state != MWBase::StateManager::State_Running) + continue; + mActors.updateProcessingRange(); // Update mechanics for new processing range immediately diff --git a/files/mygui/openmw_settings_window.layout b/files/mygui/openmw_settings_window.layout index 8e6e98612..3c2664857 100644 --- a/files/mygui/openmw_settings_window.layout +++ b/files/mygui/openmw_settings_window.layout @@ -75,7 +75,7 @@ - + @@ -87,7 +87,7 @@ - + @@ -224,7 +224,7 @@ - + From 4b1d287edaba1f257d5cacbf1b8cfdccc9d939ca Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 28 Oct 2018 17:03:38 +0300 Subject: [PATCH 155/163] Clean up redundant getClass calls in classes --- apps/openmw/mwclass/armor.cpp | 2 +- apps/openmw/mwclass/clothing.cpp | 2 +- apps/openmw/mwclass/creature.cpp | 19 +++--- apps/openmw/mwclass/npc.cpp | 104 +++++++++++++++---------------- apps/openmw/mwclass/weapon.cpp | 2 +- 5 files changed, 62 insertions(+), 67 deletions(-) diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index ad64ad0d1..0a4958eb9 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -301,7 +301,7 @@ namespace MWClass return std::make_pair(0, "#{sInventoryMessage1}"); // slots that this item can be equipped in - std::pair, bool> slots_ = ptr.getClass().getEquipmentSlots(ptr); + std::pair, bool> slots_ = getEquipmentSlots(ptr); if (slots_.first.empty()) return std::make_pair(0, ""); diff --git a/apps/openmw/mwclass/clothing.cpp b/apps/openmw/mwclass/clothing.cpp index 027882304..21c0d89c2 100644 --- a/apps/openmw/mwclass/clothing.cpp +++ b/apps/openmw/mwclass/clothing.cpp @@ -215,7 +215,7 @@ namespace MWClass std::pair Clothing::canBeEquipped(const MWWorld::ConstPtr &ptr, const MWWorld::Ptr &npc) const { // slots that this item can be equipped in - std::pair, bool> slots_ = ptr.getClass().getEquipmentSlots(ptr); + std::pair, bool> slots_ = getEquipmentSlots(ptr); if (slots_.first.empty()) return std::make_pair(0, ""); diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index e03635b0c..8a6f7d23d 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -137,7 +137,7 @@ namespace MWClass // Persistent actors with 0 health do not play death animation if (data->mCreatureStats.isDead()) - data->mCreatureStats.setDeathAnimationFinished(ptr.getClass().isPersistent(ptr)); + data->mCreatureStats.setDeathAnimationFinished(isPersistent(ptr)); // spells for (std::vector::const_iterator iter (ref->mBase->mSpells.mList.begin()); @@ -194,9 +194,9 @@ namespace MWClass models.push_back(model); // FIXME: use const version of InventoryStore functions once they are available - if (ptr.getClass().hasInventoryStore(ptr)) + if (hasInventoryStore(ptr)) { - const MWWorld::InventoryStore& invStore = ptr.getClass().getInventoryStore(ptr); + const MWWorld::InventoryStore& invStore = getInventoryStore(ptr); for (int slot = 0; slot < MWWorld::InventoryStore::Slots; ++slot) { MWWorld::ConstContainerStoreIterator equipped = invStore.getSlot(slot); @@ -237,7 +237,7 @@ namespace MWClass // Get the weapon used (if hand-to-hand, weapon = inv.end()) MWWorld::Ptr weapon; - if (ptr.getClass().hasInventoryStore(ptr)) + if (hasInventoryStore(ptr)) { MWWorld::InventoryStore &inv = getInventoryStore(ptr); MWWorld::ContainerStoreIterator weaponslot = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); @@ -253,8 +253,7 @@ namespace MWClass // For AI actors, get combat targets to use in the ray cast. Only those targets will return a positive hit result. std::vector targetActors; - if (!ptr.isEmpty() && ptr.getClass().isActor()) - ptr.getClass().getCreatureStats(ptr).getAiSequence().getCombatTargets(targetActors); + stats.getAiSequence().getCombatTargets(targetActors); std::pair result = MWBase::Environment::get().getWorld()->getHitContact(ptr, dist, targetActors); if (result.first.isEmpty()) @@ -332,7 +331,7 @@ namespace MWClass void Creature::onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, const osg::Vec3f &hitPosition, bool successful) const { - MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats(ptr); + MWMechanics::CreatureStats& stats = getCreatureStats(ptr); // NOTE: 'object' and/or 'attacker' may be empty. if (!attacker.isEmpty() && attacker.getClass().isActor() && !stats.getAiSequence().isInCombat(attacker)) @@ -346,7 +345,7 @@ namespace MWClass setOnPcHitMe = MWBase::Environment::get().getMechanicsManager()->actorAttacked(ptr, attacker); // Attacker and target store each other as hitattemptactor if they have no one stored yet - if (!attacker.isEmpty() && attacker.getClass().isActor() && !ptr.isEmpty() && ptr.getClass().isActor()) + if (!attacker.isEmpty() && attacker.getClass().isActor()) { MWMechanics::CreatureStats& statsAttacker = attacker.getClass().getCreatureStats(attacker); // First handle the attacked actor @@ -522,7 +521,7 @@ namespace MWClass const MWBase::World *world = MWBase::Environment::get().getWorld(); const MWMechanics::MagicEffects &mageffects = stats.getMagicEffects(); - bool running = ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run); + bool running = stats.getStance(MWMechanics::CreatureStats::Stance_Run); // The Run speed difference for creatures comes from the animation speed difference (see runStateToWalkState in character.cpp) float runSpeed = walkSpeed; @@ -808,7 +807,7 @@ namespace MWClass void Creature::respawn(const MWWorld::Ptr &ptr) const { - const MWMechanics::CreatureStats& creatureStats = ptr.getClass().getCreatureStats(ptr); + const MWMechanics::CreatureStats& creatureStats = getCreatureStats(ptr); if (ptr.getRefData().getCount() > 0 && !creatureStats.isDead()) return; diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index b8b1e9600..ec0671afc 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -357,7 +357,7 @@ namespace MWClass // Persistent actors with 0 health do not play death animation if (data->mNpcStats.isDead()) - data->mNpcStats.setDeathAnimationFinished(ptr.getClass().isPersistent(ptr)); + data->mNpcStats.setDeathAnimationFinished(isPersistent(ptr)); // race powers const ESM::Race *race = MWBase::Environment::get().getWorld()->getStore().get().find(ref->mBase->mRace); @@ -469,41 +469,38 @@ namespace MWClass // FIXME: use const version of InventoryStore functions once they are available // preload equipped items - if (ptr.getClass().hasInventoryStore(ptr)) + const MWWorld::InventoryStore& invStore = getInventoryStore(ptr); + for (int slot = 0; slot < MWWorld::InventoryStore::Slots; ++slot) { - const MWWorld::InventoryStore& invStore = ptr.getClass().getInventoryStore(ptr); - for (int slot = 0; slot < MWWorld::InventoryStore::Slots; ++slot) + MWWorld::ConstContainerStoreIterator equipped = invStore.getSlot(slot); + if (equipped != invStore.end()) { - MWWorld::ConstContainerStoreIterator equipped = invStore.getSlot(slot); - if (equipped != invStore.end()) + std::vector parts; + if(equipped->getTypeName() == typeid(ESM::Clothing).name()) { - std::vector parts; - if(equipped->getTypeName() == typeid(ESM::Clothing).name()) - { - const ESM::Clothing *clothes = equipped->get()->mBase; - parts = clothes->mParts.mParts; - } - else if(equipped->getTypeName() == typeid(ESM::Armor).name()) - { - const ESM::Armor *armor = equipped->get()->mBase; - parts = armor->mParts.mParts; - } - else - { - std::string model = equipped->getClass().getModel(*equipped); - if (!model.empty()) - models.push_back(model); - } + const ESM::Clothing *clothes = equipped->get()->mBase; + parts = clothes->mParts.mParts; + } + else if(equipped->getTypeName() == typeid(ESM::Armor).name()) + { + const ESM::Armor *armor = equipped->get()->mBase; + parts = armor->mParts.mParts; + } + else + { + std::string model = equipped->getClass().getModel(*equipped); + if (!model.empty()) + models.push_back(model); + } - for (std::vector::const_iterator it = parts.begin(); it != parts.end(); ++it) - { - std::string partname = female ? it->mFemale : it->mMale; - if (partname.empty()) - partname = female ? it->mMale : it->mFemale; - const ESM::BodyPart* part = MWBase::Environment::get().getWorld()->getStore().get().search(partname); - if (part && !part->mModel.empty()) - models.push_back("meshes/"+part->mModel); - } + for (std::vector::const_iterator it = parts.begin(); it != parts.end(); ++it) + { + std::string partname = female ? it->mFemale : it->mMale; + if (partname.empty()) + partname = female ? it->mMale : it->mFemale; + const ESM::BodyPart* part = MWBase::Environment::get().getWorld()->getStore().get().search(partname); + if (part && !part->mModel.empty()) + models.push_back("meshes/"+part->mModel); } } } @@ -573,8 +570,8 @@ namespace MWClass // For AI actors, get combat targets to use in the ray cast. Only those targets will return a positive hit result. std::vector targetActors; - if (!ptr.isEmpty() && ptr.getClass().isActor() && ptr != MWMechanics::getPlayer()) - ptr.getClass().getCreatureStats(ptr).getAiSequence().getCombatTargets(targetActors); + if (ptr != MWMechanics::getPlayer()) + getCreatureStats(ptr).getAiSequence().getCombatTargets(targetActors); // TODO: Use second to work out the hit angle std::pair result = world->getHitContact(ptr, dist, targetActors); @@ -597,7 +594,7 @@ namespace MWClass if(!weapon.isEmpty()) weapskill = weapon.getClass().getEquipmentSkill(weapon); - float hitchance = MWMechanics::getHitChance(ptr, victim, ptr.getClass().getSkill(ptr, weapskill)); + float hitchance = MWMechanics::getHitChance(ptr, victim, getSkill(ptr, weapskill)); if (Misc::Rng::roll0to99() >= hitchance) { @@ -667,7 +664,7 @@ namespace MWClass void Npc::onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, const osg::Vec3f &hitPosition, bool successful) const { MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); - MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats(ptr); + MWMechanics::CreatureStats& stats = getCreatureStats(ptr); bool wasDead = stats.isDead(); // Note OnPcHitMe is not set for friendly hits. @@ -681,7 +678,7 @@ namespace MWClass } // Attacker and target store each other as hitattemptactor if they have no one stored yet - if (!attacker.isEmpty() && attacker.getClass().isActor() && !ptr.isEmpty() && ptr.getClass().isActor()) + if (!attacker.isEmpty() && attacker.getClass().isActor()) { MWMechanics::CreatureStats& statsAttacker = attacker.getClass().getCreatureStats(attacker); // First handle the attacked actor @@ -702,7 +699,7 @@ namespace MWClass if (setOnPcHitMe && !attacker.isEmpty() && attacker == MWMechanics::getPlayer()) { - const std::string &script = ptr.getClass().getScript(ptr); + const std::string &script = getScript(ptr); /* Set the OnPCHitMe script variable. The script is responsible for clearing it. */ if(!script.empty()) ptr.getRefData().getLocals().setVarByInt(script, "onpchitme", 1); @@ -891,12 +888,11 @@ namespace MWClass if(stats.getAiSequence().isInCombat()) return std::shared_ptr(new MWWorld::FailedAction("")); - if(getCreatureStats(actor).getStance(MWMechanics::CreatureStats::Stance_Sneak) - || ptr.getClass().getCreatureStats(ptr).getKnockedDown()) + if(getCreatureStats(actor).getStance(MWMechanics::CreatureStats::Stance_Sneak) || stats.getKnockedDown()) return std::shared_ptr(new MWWorld::ActionOpen(ptr)); // stealing // Can't talk to werewolfs - if(ptr.getClass().isNpc() && ptr.getClass().getNpcStats(ptr).isWerewolf()) + if(getNpcStats(ptr).isWerewolf()) return std::shared_ptr (new MWWorld::FailedAction("")); return std::shared_ptr(new MWWorld::ActionTalk(ptr)); @@ -927,7 +923,7 @@ namespace MWClass float Npc::getSpeed(const MWWorld::Ptr& ptr) const { - const MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats(ptr); + const MWMechanics::CreatureStats& stats = getCreatureStats(ptr); if (stats.isParalyzed() || stats.getKnockedDown() || stats.isDead()) return 0.f; @@ -993,7 +989,7 @@ namespace MWClass if(getEncumbrance(ptr) > getCapacity(ptr)) return 0.f; - const MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats(ptr); + const MWMechanics::CreatureStats& stats = getCreatureStats(ptr); if (stats.isParalyzed() || stats.getKnockedDown() || stats.isDead()) return 0.f; @@ -1213,13 +1209,13 @@ namespace MWClass return (name == "left") ? "FootWaterLeft" : "FootWaterRight"; if(world->isOnGround(ptr)) { - if (ptr.getClass().getNpcStats(ptr).isWerewolf() - && ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run)) + if (getNpcStats(ptr).isWerewolf() + && getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run)) { MWMechanics::WeaponType weaponType = MWMechanics::WeapType_None; - MWMechanics::getActiveWeapon(ptr.getClass().getCreatureStats(ptr), ptr.getClass().getInventoryStore(ptr), &weaponType); + MWMechanics::getActiveWeapon(getCreatureStats(ptr), getInventoryStore(ptr), &weaponType); if (weaponType == MWMechanics::WeapType_None) - return ""; + return std::string(); } const MWWorld::InventoryStore &inv = Npc::getInventoryStore(ptr); @@ -1237,12 +1233,12 @@ namespace MWClass return (name == "left") ? "FootHeavyLeft" : "FootHeavyRight"; } } - return ""; + return std::string(); } // Morrowind ignores land soundgen for NPCs if(name == "land") - return ""; + return std::string(); if(name == "swimleft") return "Swim Left"; if(name == "swimright") @@ -1252,11 +1248,11 @@ namespace MWClass // only for biped creatures? if(name == "moan") - return ""; + return std::string(); if(name == "roar") - return ""; + return std::string(); if(name == "scream") - return ""; + return std::string(); throw std::runtime_error(std::string("Unexpected soundgen type: ")+name); } @@ -1270,7 +1266,7 @@ namespace MWClass int Npc::getSkill(const MWWorld::Ptr& ptr, int skill) const { - return ptr.getClass().getNpcStats(ptr).getSkill(skill).getModified(); + return getNpcStats(ptr).getSkill(skill).getModified(); } int Npc::getBloodTexture(const MWWorld::ConstPtr &ptr) const @@ -1352,7 +1348,7 @@ namespace MWClass void Npc::respawn(const MWWorld::Ptr &ptr) const { - const MWMechanics::CreatureStats& creatureStats = ptr.getClass().getCreatureStats(ptr); + const MWMechanics::CreatureStats& creatureStats = getCreatureStats(ptr); if (ptr.getRefData().getCount() > 0 && !creatureStats.isDead()) return; diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index 7d28c8983..b45e7cd2c 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -391,7 +391,7 @@ namespace MWClass && MWBase::Environment::get().getWindowManager()->isGuiMode()) return std::make_pair(0, "#{sCantEquipWeapWarning}"); - std::pair, bool> slots_ = ptr.getClass().getEquipmentSlots(ptr); + std::pair, bool> slots_ = getEquipmentSlots(ptr); if (slots_.first.empty()) return std::make_pair (0, ""); From 77b0ff7a752e858524e868940b7a44f8dd7fe95c Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 28 Oct 2018 18:28:36 +0300 Subject: [PATCH 156/163] Use real thrown weapon damage in tooltips and weapon rating (feature #4697) --- CHANGELOG.md | 1 + apps/openmw/mwclass/weapon.cpp | 12 ++++++++++-- apps/openmw/mwmechanics/weaponpriority.cpp | 10 +++++++++- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5bc654e10..f373b375d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -189,6 +189,7 @@ Feature #4642: Batching potion creation Feature #4647: Cull actors outside of AI processing range Feature #4682: Use the collision box from basic creature mesh if the X one have no collisions + Feature #4697: Use the real thrown weapon damage in tooltips and AI Task #2490: Don't open command prompt window on Release-mode builds automatically Task #4545: Enable is_pod string test Task #4605: Optimize skinning diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index 7d28c8983..b5035fb3c 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -265,7 +265,7 @@ namespace MWClass std::string text; // weapon type & damage - if ((ref->mBase->mData.mType < 12 || Settings::Manager::getBool("show projectile damage", "Game")) && ref->mBase->mData.mType < 14) + if ((ref->mBase->mData.mType < ESM::Weapon::Arrow || Settings::Manager::getBool("show projectile damage", "Game")) && ref->mBase->mData.mType <= ESM::Weapon::Bolt) { text += "\n#{sType} "; @@ -295,7 +295,15 @@ namespace MWClass ((oneOrTwoHanded != "") ? ", " + store.get().find(oneOrTwoHanded)->mValue.getString() : ""); // weapon damage - if (ref->mBase->mData.mType >= 9) + if (ref->mBase->mData.mType == ESM::Weapon::MarksmanThrown) + { + // Thrown weapons have 2x real damage applied + // as they're both the weapon and the ammo + text += "\n#{sAttack}: " + + MWGui::ToolTips::toString(static_cast(ref->mBase->mData.mChop[0] * 2)) + + " - " + MWGui::ToolTips::toString(static_cast(ref->mBase->mData.mChop[1] * 2)); + } + else if (ref->mBase->mData.mType >= ESM::Weapon::MarksmanBow) { // marksman text += "\n#{sAttack}: " diff --git a/apps/openmw/mwmechanics/weaponpriority.cpp b/apps/openmw/mwmechanics/weaponpriority.cpp index aaa06f6e4..55fb2eaf0 100644 --- a/apps/openmw/mwmechanics/weaponpriority.cpp +++ b/apps/openmw/mwmechanics/weaponpriority.cpp @@ -58,8 +58,16 @@ namespace MWMechanics } const float chop = (weapon->mData.mChop[0] + weapon->mData.mChop[1]) / 2.f; - if (weapon->mData.mType >= ESM::Weapon::MarksmanBow) + // We need to account for the fact that thrown weapons have 2x real damage applied to the target + // as they're both the weapon and the ammo of the hit + if (weapon->mData.mType == ESM::Weapon::MarksmanThrown) + { + rating = chop * 2; + } + else if (weapon->mData.mType >= ESM::Weapon::MarksmanBow) + { rating = chop; + } else { const float slash = (weapon->mData.mSlash[0] + weapon->mData.mSlash[1]) / 2.f; From 89b8eb029f6a21726fb426abda13ef5ee3f8615e Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 27 Oct 2018 19:35:19 +0300 Subject: [PATCH 157/163] Add icons for record context menu actions --- apps/opencs/view/world/table.cpp | 11 ++++++++++- files/opencs/record-down.png | Bin 0 -> 150 bytes files/opencs/record-up.png | Bin 0 -> 150 bytes files/opencs/resources.qrc | 2 ++ 4 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 files/opencs/record-down.png create mode 100644 files/opencs/record-up.png diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 986717cb2..f625fbe8d 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -300,12 +300,14 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, { mCreateAction = new QAction (tr ("Add Record"), this); connect (mCreateAction, SIGNAL (triggered()), this, SIGNAL (createRequest())); + mCreateAction->setIcon(QIcon(":edit-add")); addAction (mCreateAction); CSMPrefs::Shortcut* createShortcut = new CSMPrefs::Shortcut("table-add", this); createShortcut->associateAction(mCreateAction); mCloneAction = new QAction (tr ("Clone Record"), this); connect(mCloneAction, SIGNAL (triggered()), this, SLOT (cloneRecord())); + mCloneAction->setIcon(QIcon(":edit-clone")); addAction(mCloneAction); CSMPrefs::Shortcut* cloneShortcut = new CSMPrefs::Shortcut("table-clone", this); cloneShortcut->associateAction(mCloneAction); @@ -322,49 +324,56 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, mRevertAction = new QAction (tr ("Revert Record"), this); connect (mRevertAction, SIGNAL (triggered()), mDispatcher, SLOT (executeRevert())); + mRevertAction->setIcon(QIcon(":edit-undo")); addAction (mRevertAction); CSMPrefs::Shortcut* revertShortcut = new CSMPrefs::Shortcut("table-revert", this); revertShortcut->associateAction(mRevertAction); mDeleteAction = new QAction (tr ("Delete Record"), this); connect (mDeleteAction, SIGNAL (triggered()), mDispatcher, SLOT (executeDelete())); + mDeleteAction->setIcon(QIcon(":edit-delete")); addAction (mDeleteAction); CSMPrefs::Shortcut* deleteShortcut = new CSMPrefs::Shortcut("table-remove", this); deleteShortcut->associateAction(mDeleteAction); - mMoveUpAction = new QAction (tr ("Move Up"), this); connect (mMoveUpAction, SIGNAL (triggered()), this, SLOT (moveUpRecord())); + mMoveUpAction->setIcon(QIcon(":record-up")); addAction (mMoveUpAction); CSMPrefs::Shortcut* moveUpShortcut = new CSMPrefs::Shortcut("table-moveup", this); moveUpShortcut->associateAction(mMoveUpAction); mMoveDownAction = new QAction (tr ("Move Down"), this); connect (mMoveDownAction, SIGNAL (triggered()), this, SLOT (moveDownRecord())); + mMoveDownAction->setIcon(QIcon(":record-down")); addAction (mMoveDownAction); CSMPrefs::Shortcut* moveDownShortcut = new CSMPrefs::Shortcut("table-movedown", this); moveDownShortcut->associateAction(mMoveDownAction); mViewAction = new QAction (tr ("View"), this); connect (mViewAction, SIGNAL (triggered()), this, SLOT (viewRecord())); + mViewAction->setIcon(QIcon(":/cell.png")); addAction (mViewAction); CSMPrefs::Shortcut* viewShortcut = new CSMPrefs::Shortcut("table-view", this); viewShortcut->associateAction(mViewAction); mPreviewAction = new QAction (tr ("Preview"), this); connect (mPreviewAction, SIGNAL (triggered()), this, SLOT (previewRecord())); + mPreviewAction->setIcon(QIcon(":edit-preview")); addAction (mPreviewAction); CSMPrefs::Shortcut* previewShortcut = new CSMPrefs::Shortcut("table-preview", this); previewShortcut->associateAction(mPreviewAction); mExtendedDeleteAction = new QAction (tr ("Extended Delete Record"), this); connect (mExtendedDeleteAction, SIGNAL (triggered()), this, SLOT (executeExtendedDelete())); + mExtendedDeleteAction->setIcon(QIcon(":edit-delete")); addAction (mExtendedDeleteAction); CSMPrefs::Shortcut* extendedDeleteShortcut = new CSMPrefs::Shortcut("table-extendeddelete", this); extendedDeleteShortcut->associateAction(mExtendedDeleteAction); mExtendedRevertAction = new QAction (tr ("Extended Revert Record"), this); connect (mExtendedRevertAction, SIGNAL (triggered()), this, SLOT (executeExtendedRevert())); + mExtendedRevertAction->setIcon(QIcon(":edit-undo")); addAction (mExtendedRevertAction); CSMPrefs::Shortcut* extendedRevertShortcut = new CSMPrefs::Shortcut("table-extendedrevert", this); extendedRevertShortcut->associateAction(mExtendedRevertAction); diff --git a/files/opencs/record-down.png b/files/opencs/record-down.png new file mode 100644 index 0000000000000000000000000000000000000000..92fe788602136102ed33598031e7493325540320 GIT binary patch literal 150 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6Vm)0PLp07OCrD@=n9jLfMwywJ zxjA1t`04)tJv}Rg{(my}cbakW(Qm$^Xa4W2d~t2d&$@4|8{>bAB^7(_ejmvO1ov;B xxTNvzSDAr@K~2Jq-8-eX*H_hev&U;NGbG-S&@#X6+Yhve!PC{xWt~$(695OAIXeIV literal 0 HcmV?d00001 diff --git a/files/opencs/record-up.png b/files/opencs/record-up.png new file mode 100644 index 0000000000000000000000000000000000000000..c8bfa4a34274561e236984ebc4d486d05017bd71 GIT binary patch literal 150 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6Vm)0PLp07OCrDHr=sETEZQ%9& zr98`-?(?x(vuT!khNyD9EW5v%2MG3UujSt8x&3F|x7LmE$r>MXKi0M%`u~15d!nIJ xiE-$^-{((stZA9}F#g{&PlK;MJkHV#46n~i6gX-JZv|S!;OXk;vd$@?2>`lgIN|^R literal 0 HcmV?d00001 diff --git a/files/opencs/resources.qrc b/files/opencs/resources.qrc index 4fc4bdf28..c7be70120 100644 --- a/files/opencs/resources.qrc +++ b/files/opencs/resources.qrc @@ -90,6 +90,8 @@ multitype.png record-next.png record-previous.png + record-down.png + record-up.png record-delete.png record-preview.png record-clone.png From 8757b3edd1d438ef1bf522d099d8a79b68a28a4c Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 28 Oct 2018 20:44:03 +0300 Subject: [PATCH 158/163] Add icons for Edit and Touch actions (kudos to akortunov) --- apps/opencs/view/world/table.cpp | 2 ++ files/opencs/record-edit.png | Bin 0 -> 220 bytes files/opencs/record-touch.png | Bin 0 -> 289 bytes files/opencs/resources.qrc | 2 ++ 4 files changed, 4 insertions(+) create mode 100644 files/opencs/record-edit.png create mode 100644 files/opencs/record-touch.png diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index f625fbe8d..14df4658d 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -292,6 +292,7 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, mEditAction = new QAction (tr ("Edit Record"), this); connect (mEditAction, SIGNAL (triggered()), this, SLOT (editRecord())); + mEditAction->setIcon(QIcon(":edit-edit")); addAction (mEditAction); CSMPrefs::Shortcut* editShortcut = new CSMPrefs::Shortcut("table-edit", this); editShortcut->associateAction(mEditAction); @@ -317,6 +318,7 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, { mTouchAction = new QAction(tr("Touch Record"), this); connect(mTouchAction, SIGNAL(triggered()), this, SLOT(touchRecord())); + mTouchAction->setIcon(QIcon(":edit-touch")); addAction(mTouchAction); CSMPrefs::Shortcut* touchShortcut = new CSMPrefs::Shortcut("table-touch", this); touchShortcut->associateAction(mTouchAction); diff --git a/files/opencs/record-edit.png b/files/opencs/record-edit.png new file mode 100644 index 0000000000000000000000000000000000000000..8b94e163410880ebd3efbeb68b3aa23e595d2c65 GIT binary patch literal 220 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf67JIrlhG?8mPEcTwkd%;+`18+M zl81-q|HO#dKz@Y69AAULw*Lit|NRHa&fti2+{NUk5t6#=+QaYj4>GoMYWeW2EjKeX z`0+OVpyuBr?{BDSi`uizFn>6SN6Sa+Ezixm|B{PEE!@CU zBv}dg!@$fxGe5Jl3>zXWow}|Qgb-FXq?AD0w%-ev0ww6yZf*L6{}V{jEJd?F38ZM4 zqTL+!vbjHR_fj-0nP7}biZ0PdKl_uM`%+wiGu)x+a1GvZv37z`dBh>+9eR#~Precord-preview.png record-clone.png record-add.png + record-edit.png + record-touch.png record-revert.png resources-icon.png resources-mesh.png From 434b1fd0e351e569235b46fda5500d3e66845833 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Mon, 29 Oct 2018 01:53:49 +0300 Subject: [PATCH 159/163] Use Shift-C and Shift-V keybindings for View and Preview (feature #2845) --- CHANGELOG.md | 1 + apps/opencs/model/prefs/state.cpp | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5bc654e10..891721f5e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -158,6 +158,7 @@ Feature #1645: Casting effects from objects Feature #2606: Editor: Implemented (optional) case sensitive global search Feature #2787: Use the autogenerated collision box, if the creature mesh has no predefined one + Feature #2845: Editor: add record view and preview default keybindings Feature #2847: Content selector: allow to copy the path to a file by using the context menu 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/opencs/model/prefs/state.cpp b/apps/opencs/model/prefs/state.cpp index b6a55ea86..57d47ebbc 100644 --- a/apps/opencs/model/prefs/state.cpp +++ b/apps/opencs/model/prefs/state.cpp @@ -317,8 +317,8 @@ void CSMPrefs::State::declare() declareShortcut ("table-remove", "Remove Row/Record", QKeySequence(Qt::Key_Delete)); declareShortcut ("table-moveup", "Move Record Up", QKeySequence()); declareShortcut ("table-movedown", "Move Record Down", QKeySequence()); - declareShortcut ("table-view", "View Record", QKeySequence()); - declareShortcut ("table-preview", "Preview Record", QKeySequence()); + declareShortcut ("table-view", "View Record", QKeySequence(Qt::ShiftModifier | Qt::Key_C)); + declareShortcut ("table-preview", "Preview Record", QKeySequence(Qt::ShiftModifier | Qt::Key_V)); declareShortcut ("table-extendeddelete", "Extended Record Deletion", QKeySequence()); declareShortcut ("table-extendedrevert", "Extended Record Revertion", QKeySequence()); From e45c0c8f59f69703368233be714f5e13680de4dc Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Mon, 29 Oct 2018 16:28:24 +0000 Subject: [PATCH 160/163] Update to new packages --- .gitlab-ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 565feff14..904ab8ffb 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -17,9 +17,9 @@ Debian: # - apt-get install -y libmygui-dev libbullet-dev # to be updated to latest below because stretch is too old - curl http://ftp.us.debian.org/debian/pool/main/b/bullet/libbullet-dev_2.87+dfsg-2_amd64.deb -o libbullet-dev_2.87+dfsg-2_amd64.deb - curl http://ftp.us.debian.org/debian/pool/main/b/bullet/libbullet2.87_2.87+dfsg-2_amd64.deb -o libbullet2.87_2.87+dfsg-2_amd64.deb - - curl http://ftp.us.debian.org/debian/pool/main/m/mygui/libmygui.openglplatform0debian1v5_3.2.2+dfsg-1_amd64.deb -o libmygui.openglplatform0debian1v5_3.2.2+dfsg-1_amd64.deb - - curl http://ftp.us.debian.org/debian/pool/main/m/mygui/libmyguiengine3debian1v5_3.2.2+dfsg-1_amd64.deb -o libmyguiengine3debian1v5_3.2.2+dfsg-1_amd64.deb - - curl http://ftp.us.debian.org/debian/pool/main/m/mygui/libmygui-dev_3.2.2+dfsg-1_amd64.deb -o libmygui-dev_3.2.2+dfsg-1_amd64.deb + - curl http://ftp.us.debian.org/debian/pool/main/m/mygui/libmygui.openglplatform0debian1v5_3.2.2+dfsg-2_amd64.deb -o libmygui.openglplatform0debian1v5_3.2.2+dfsg-2_amd64.deb + - curl http://ftp.us.debian.org/debian/pool/main/m/mygui/libmyguiengine3debian1v5_3.2.2+dfsg-2_amd64.deb -o libmyguiengine3debian1v5_3.2.2+dfsg-2_amd64.deb + - curl http://ftp.us.debian.org/debian/pool/main/m/mygui/libmygui-dev_3.2.2+dfsg-2_amd64.deb -o libmygui-dev_3.2.2+dfsg-2_amd64.deb - dpkg --ignore-depends=libmygui.ogreplatform0debian1v5 -i *.deb stage: build script: From cc1fd8c92e6fd206aaaa238e1ab5096688eb4e14 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Mon, 29 Oct 2018 17:35:04 +0000 Subject: [PATCH 161/163] Use https://http.kali.org/pool/main/m/mygui/ instead since debian doesn't keep old packages. --- .gitlab-ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 904ab8ffb..79cb6957c 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -17,9 +17,9 @@ Debian: # - apt-get install -y libmygui-dev libbullet-dev # to be updated to latest below because stretch is too old - curl http://ftp.us.debian.org/debian/pool/main/b/bullet/libbullet-dev_2.87+dfsg-2_amd64.deb -o libbullet-dev_2.87+dfsg-2_amd64.deb - curl http://ftp.us.debian.org/debian/pool/main/b/bullet/libbullet2.87_2.87+dfsg-2_amd64.deb -o libbullet2.87_2.87+dfsg-2_amd64.deb - - curl http://ftp.us.debian.org/debian/pool/main/m/mygui/libmygui.openglplatform0debian1v5_3.2.2+dfsg-2_amd64.deb -o libmygui.openglplatform0debian1v5_3.2.2+dfsg-2_amd64.deb - - curl http://ftp.us.debian.org/debian/pool/main/m/mygui/libmyguiengine3debian1v5_3.2.2+dfsg-2_amd64.deb -o libmyguiengine3debian1v5_3.2.2+dfsg-2_amd64.deb - - curl http://ftp.us.debian.org/debian/pool/main/m/mygui/libmygui-dev_3.2.2+dfsg-2_amd64.deb -o libmygui-dev_3.2.2+dfsg-2_amd64.deb + - curl https://http.kali.org/pool/main/m/mygui/libmygui.openglplatform0debian1v5_3.2.2+dfsg-1_amd64.deb -o libmygui.openglplatform0debian1v5_3.2.2+dfsg-1_amd64.deb + - curl https://http.kali.org/pool/main/m/mygui/libmyguiengine3debian1v5_3.2.2+dfsg-1_amd64.deb -o libmyguiengine3debian1v5_3.2.2+dfsg-1_amd64.deb + - curl https://http.kali.org/pool/main/m/mygui/libmygui-dev_3.2.2+dfsg-1_amd64.deb -o libmygui-dev_3.2.2+dfsg-1_amd64.deb - dpkg --ignore-depends=libmygui.ogreplatform0debian1v5 -i *.deb stage: build script: From 23d273b1d739ea007e726a5f3821142f750ecbcd Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Mon, 29 Oct 2018 17:43:08 +0000 Subject: [PATCH 162/163] Follow the redirects --- .gitlab-ci.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 79cb6957c..06fab6cce 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -15,11 +15,11 @@ Debian: - apt-get update -yq - apt-get -o dir::cache::archives="$APT_CACHE_DIR" install -y cmake libboost-filesystem-dev libboost-program-options-dev libboost-system-dev libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libswresample-dev libsdl2-dev libqt4-dev libopenal-dev libopenscenegraph-3.4-dev libunshield-dev libtinyxml-dev # - apt-get install -y libmygui-dev libbullet-dev # to be updated to latest below because stretch is too old - - curl http://ftp.us.debian.org/debian/pool/main/b/bullet/libbullet-dev_2.87+dfsg-2_amd64.deb -o libbullet-dev_2.87+dfsg-2_amd64.deb - - curl http://ftp.us.debian.org/debian/pool/main/b/bullet/libbullet2.87_2.87+dfsg-2_amd64.deb -o libbullet2.87_2.87+dfsg-2_amd64.deb - - curl https://http.kali.org/pool/main/m/mygui/libmygui.openglplatform0debian1v5_3.2.2+dfsg-1_amd64.deb -o libmygui.openglplatform0debian1v5_3.2.2+dfsg-1_amd64.deb - - curl https://http.kali.org/pool/main/m/mygui/libmyguiengine3debian1v5_3.2.2+dfsg-1_amd64.deb -o libmyguiengine3debian1v5_3.2.2+dfsg-1_amd64.deb - - curl https://http.kali.org/pool/main/m/mygui/libmygui-dev_3.2.2+dfsg-1_amd64.deb -o libmygui-dev_3.2.2+dfsg-1_amd64.deb + - curl -L http://ftp.us.debian.org/debian/pool/main/b/bullet/libbullet-dev_2.87+dfsg-2_amd64.deb -o libbullet-dev_2.87+dfsg-2_amd64.deb + - curl -L http://ftp.us.debian.org/debian/pool/main/b/bullet/libbullet2.87_2.87+dfsg-2_amd64.deb -o libbullet2.87_2.87+dfsg-2_amd64.deb + - curl -L https://http.kali.org/pool/main/m/mygui/libmygui.openglplatform0debian1v5_3.2.2+dfsg-1_amd64.deb -o libmygui.openglplatform0debian1v5_3.2.2+dfsg-1_amd64.deb + - curl -L https://http.kali.org/pool/main/m/mygui/libmyguiengine3debian1v5_3.2.2+dfsg-1_amd64.deb -o libmyguiengine3debian1v5_3.2.2+dfsg-1_amd64.deb + - curl -L https://http.kali.org/pool/main/m/mygui/libmygui-dev_3.2.2+dfsg-1_amd64.deb -o libmygui-dev_3.2.2+dfsg-1_amd64.deb - dpkg --ignore-depends=libmygui.ogreplatform0debian1v5 -i *.deb stage: build script: From f98fc8d7b4db55be7cfeea927649553537ff21b6 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Mon, 29 Oct 2018 22:47:04 +0400 Subject: [PATCH 163/163] Make sure we reset current weapon animation when resurrect actor (addition to bug #2626) --- apps/openmw/mwmechanics/character.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index d5e22cb07..cdb2fdc7c 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -2523,6 +2523,7 @@ void CharacterController::resurrect() mAnimation->disable(mCurrentDeath); mCurrentDeath.clear(); mDeathState = CharState_None; + mWeaponType = WeapType_None; } void CharacterController::updateContinuousVfx()