diff --git a/apps/openmw/mwgui/journalbooks.cpp b/apps/openmw/mwgui/journalbooks.cpp index a1d74fab6..f3ee8162e 100644 --- a/apps/openmw/mwgui/journalbooks.cpp +++ b/apps/openmw/mwgui/journalbooks.cpp @@ -5,8 +5,10 @@ #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" -#include "textcolours.hpp" +#include +#include +#include "textcolours.hpp" namespace { @@ -154,8 +156,8 @@ MWGui::BookTypesetter::Utf8Span to_utf8_span (char const * text) typedef TypesetBook::Ptr book; -JournalBooks::JournalBooks (JournalViewModel::Ptr model) : - mModel (model) +JournalBooks::JournalBooks (JournalViewModel::Ptr model, ToUTF8::FromType encoding) : + mModel (model), mEncoding(encoding) { } @@ -217,34 +219,82 @@ book JournalBooks::createQuestBook (const std::string& questName) } book JournalBooks::createTopicIndexBook () +{ + bool isRussian = (mEncoding == ToUTF8::WINDOWS_1251); + + BookTypesetter::Ptr typesetter = isRussian ? createCyrillicJournalIndex() : createLatinJournalIndex(); + + return typesetter->complete (); +} + +BookTypesetter::Ptr JournalBooks::createLatinJournalIndex () { BookTypesetter::Ptr typesetter = BookTypesetter::create (92, 250); typesetter->setSectionAlignment (BookTypesetter::AlignCenter); - BookTypesetter::Style* body = typesetter->createStyle ("", MyGUI::Colour::Black); + char ch = 'A'; + BookTypesetter::Style* body = typesetter->createStyle ("", MyGUI::Colour::Black); for (int i = 0; i < 26; ++i) { - char ch = 'A' + i; - char buffer [32]; - sprintf (buffer, "( %c )", ch); const MWGui::TextColours& textColours = MWBase::Environment::get().getWindowManager()->getTextColours(); BookTypesetter::Style* style = typesetter->createHotStyle (body, textColours.journalTopic, textColours.journalTopicOver, - textColours.journalTopicPressed, ch); + textColours.journalTopicPressed, (uint32_t) ch); if (i == 13) typesetter->sectionBreak (); typesetter->write (style, to_utf8_span (buffer)); typesetter->lineBreak (); + + ch++; } - return typesetter->complete (); + return typesetter; +} + +BookTypesetter::Ptr JournalBooks::createCyrillicJournalIndex () +{ + BookTypesetter::Ptr typesetter = BookTypesetter::create (92, 250); + + typesetter->setSectionAlignment (BookTypesetter::AlignCenter); + + BookTypesetter::Style* body = typesetter->createStyle ("", MyGUI::Colour::Black); + + unsigned char ch[2] = {0xd0, 0x90}; // CYRILLIC CAPITAL A is a 0xd090 in UTF-8 + + for (int i = 0; i < 32; ++i) + { + char buffer [32]; + sprintf(buffer, "( %c%c )", ch[0], ch[1]); + + Utf8Stream stream ((char*) ch); + uint32_t first = stream.peek(); + + const MWGui::TextColours& textColours = MWBase::Environment::get().getWindowManager()->getTextColours(); + BookTypesetter::Style* style = typesetter->createHotStyle (body, textColours.journalTopic, + textColours.journalTopicOver, + textColours.journalTopicPressed, first); + + // Words can not be started with these characters + if (i == 26 || i == 28) + continue; + + if (i == 15) + typesetter->sectionBreak (); + + typesetter->write (style, to_utf8_span (buffer)); + typesetter->lineBreak (); + + ch[1]++; + } + + return typesetter; } BookTypesetter::Ptr JournalBooks::createTypesetter () diff --git a/apps/openmw/mwgui/journalbooks.hpp b/apps/openmw/mwgui/journalbooks.hpp index 8f87825f0..aa36eecdf 100644 --- a/apps/openmw/mwgui/journalbooks.hpp +++ b/apps/openmw/mwgui/journalbooks.hpp @@ -4,6 +4,8 @@ #include "bookpage.hpp" #include "journalviewmodel.hpp" +#include + namespace MWGui { MWGui::BookTypesetter::Utf8Span to_utf8_span (char const * text); @@ -13,7 +15,7 @@ namespace MWGui typedef TypesetBook::Ptr Book; JournalViewModel::Ptr mModel; - JournalBooks (JournalViewModel::Ptr model); + JournalBooks (JournalViewModel::Ptr model, ToUTF8::FromType encoding); Book createEmptyJournalBook (); Book createJournalBook (); @@ -22,8 +24,12 @@ namespace MWGui Book createQuestBook (const std::string& questName); Book createTopicIndexBook (); + ToUTF8::FromType mEncoding; + private: BookTypesetter::Ptr createTypesetter (); + BookTypesetter::Ptr createLatinJournalIndex (); + BookTypesetter::Ptr createCyrillicJournalIndex (); }; } diff --git a/apps/openmw/mwgui/journalviewmodel.cpp b/apps/openmw/mwgui/journalviewmodel.cpp index b5d08eec6..6ff68c9c5 100644 --- a/apps/openmw/mwgui/journalviewmodel.cpp +++ b/apps/openmw/mwgui/journalviewmodel.cpp @@ -6,6 +6,8 @@ #include #include +#include +#include #include "../mwbase/world.hpp" #include "../mwbase/journal.hpp" @@ -305,18 +307,37 @@ struct JournalViewModelImpl : JournalViewModel visitor (toUtf8Span (topic.getName())); } - void visitTopicNamesStartingWith (char character, std::function < void (const std::string&) > visitor) const + void visitTopicNamesStartingWith (uint32_t character, std::function < void (const std::string&) > visitor) const { MWBase::Journal * journal = MWBase::Environment::get().getJournal(); for (MWBase::Journal::TTopicIter i = journal->topicBegin (); i != journal->topicEnd (); ++i) { - if (i->first [0] != Misc::StringUtils::toLower(character)) + Utf8Stream stream (i->first.c_str()); + uint32_t first = toUpper(stream.peek()); + + if (first != character) continue; visitor (i->second.getName()); } + } + + static uint32_t toUpper(uint32_t ch) + { + // Russian alphabet + if (ch >= 0x0430 && ch < 0x0450) + ch -= 0x20; + + // Cyrillic IO character + if (ch == 0x0451) + ch -= 0x50; + + // Latin alphabet + if (ch >= 0x61 && ch < 0x80) + ch -= 0x20; + return ch; } struct TopicEntryImpl : BaseEntry diff --git a/apps/openmw/mwgui/journalviewmodel.hpp b/apps/openmw/mwgui/journalviewmodel.hpp index 3edde3d31..01dcb49de 100644 --- a/apps/openmw/mwgui/journalviewmodel.hpp +++ b/apps/openmw/mwgui/journalviewmodel.hpp @@ -75,8 +75,8 @@ namespace MWGui /// provides the name of the topic specified by its id virtual void visitTopicName (TopicId topicId, std::function visitor) const = 0; - /// walks over the topics whose names start with the specified character providing the topics name - virtual void visitTopicNamesStartingWith (char character, std::function < void (const std::string&) > visitor) const = 0; + /// walks over the topics whose names start with the character + virtual void visitTopicNamesStartingWith (uint32_t character, std::function < void (const std::string&) > visitor) const = 0; /// walks over the topic entries for the topic specified by its identifier virtual void visitTopicEntries (TopicId topicId, std::function visitor) const = 0; diff --git a/apps/openmw/mwgui/journalwindow.cpp b/apps/openmw/mwgui/journalwindow.cpp index 3fbe44f95..4b7a789e8 100644 --- a/apps/openmw/mwgui/journalwindow.cpp +++ b/apps/openmw/mwgui/journalwindow.cpp @@ -100,8 +100,8 @@ namespace return getWidget (name); } - JournalWindowImpl (MWGui::JournalViewModel::Ptr Model, bool questList) - : JournalBooks (Model), JournalWindow() + JournalWindowImpl (MWGui::JournalViewModel::Ptr Model, bool questList, ToUTF8::FromType encoding) + : JournalBooks (Model, encoding), JournalWindow() { center(); @@ -161,6 +161,15 @@ namespace Gui::ImageButton* showActiveButton = getWidget(ShowActiveBTN); Gui::ImageButton* showAllButton = getWidget(ShowAllBTN); Gui::ImageButton* questsButton = getWidget(QuestsBTN); + + Gui::ImageButton* nextButton = getWidget(NextPageBTN); + if (nextButton->getSize().width == 64) + { + // english button has a 7 pixel wide strip of garbage on its right edge + nextButton->setSize(64-7, nextButton->getSize().height); + nextButton->setImageCoord(MyGUI::IntCoord(0,0,64-7,nextButton->getSize().height)); + } + if (!questList) { // If tribunal is not installed (-> no options button), we still want the Topics button available, @@ -176,6 +185,8 @@ namespace showActiveButton->setVisible(false); showAllButton->setVisible(false); questsButton->setVisible(false); + + adjustButton(TopicsBTN); } else { @@ -188,33 +199,24 @@ namespace adjustButton(ShowActiveBTN); adjustButton(OptionsBTN); adjustButton(QuestsBTN); - } - - Gui::ImageButton* nextButton = getWidget(NextPageBTN); - if (nextButton->getSize().width == 64) - { - // english button has a 7 pixel wide strip of garbage on its right edge - nextButton->setSize(64-7, nextButton->getSize().height); - nextButton->setImageCoord(MyGUI::IntCoord(0,0,64-7,nextButton->getSize().height)); - } - - adjustButton(TopicsBTN); - int topicsWidth = getWidget(TopicsBTN)->getSize().width; - int cancelLeft = getWidget(CancelBTN)->getPosition().left; - int cancelRight = getWidget(CancelBTN)->getPosition().left + getWidget(CancelBTN)->getSize().width; + adjustButton(TopicsBTN); + int topicsWidth = getWidget(TopicsBTN)->getSize().width; + int cancelLeft = getWidget(CancelBTN)->getPosition().left; + int cancelRight = getWidget(CancelBTN)->getPosition().left + getWidget(CancelBTN)->getSize().width; - getWidget(QuestsBTN)->setPosition(cancelRight, getWidget(QuestsBTN)->getPosition().top); + getWidget(QuestsBTN)->setPosition(cancelRight, getWidget(QuestsBTN)->getPosition().top); - // Usually Topics, Quests, and Cancel buttons have the 64px width, so we can place the Topics left-up from the Cancel button, and the Quests right-up from the Cancel button. - // But in some installations, e.g. German one, the Topics button has the 128px width, so we should place it exactly left from the Quests button. - if (getWidget(TopicsBTN)->getSize().width == 64) - { - getWidget(TopicsBTN)->setPosition(cancelLeft - topicsWidth, getWidget(TopicsBTN)->getPosition().top); - } - else - { - int questLeft = getWidget(QuestsBTN)->getPosition().left; - getWidget(TopicsBTN)->setPosition(questLeft - topicsWidth, getWidget(TopicsBTN)->getPosition().top); + // Usually Topics, Quests, and Cancel buttons have the 64px width, so we can place the Topics left-up from the Cancel button, and the Quests right-up from the Cancel button. + // But in some installations, e.g. German one, the Topics button has the 128px width, so we should place it exactly left from the Quests button. + if (topicsWidth == 64) + { + getWidget(TopicsBTN)->setPosition(cancelLeft - topicsWidth, getWidget(TopicsBTN)->getPosition().top); + } + else + { + int questLeft = getWidget(QuestsBTN)->getPosition().left; + getWidget(TopicsBTN)->setPosition(questLeft - topicsWidth, getWidget(TopicsBTN)->getPosition().top); + } } mQuestMode = false; @@ -475,7 +477,7 @@ namespace MWBase::Environment::get().getWindowManager()->playSound("book page"); } - void notifyIndexLinkClicked (MWGui::TypesetBook::InteractiveId character) + void notifyIndexLinkClicked (MWGui::TypesetBook::InteractiveId index) { setVisible (LeftTopicIndex, false); setVisible (RightTopicIndex, false); @@ -488,7 +490,7 @@ namespace AddNamesToList add(list); - mModel->visitTopicNamesStartingWith((char) character, add); + mModel->visitTopicNamesStartingWith(index, add); list->adjustSize(); @@ -643,9 +645,9 @@ namespace } // glue the implementation to the interface -MWGui::JournalWindow * MWGui::JournalWindow::create (JournalViewModel::Ptr Model, bool questList) +MWGui::JournalWindow * MWGui::JournalWindow::create (JournalViewModel::Ptr Model, bool questList, ToUTF8::FromType encoding) { - return new JournalWindowImpl (Model, questList); + return new JournalWindowImpl (Model, questList, encoding); } MWGui::JournalWindow::JournalWindow() diff --git a/apps/openmw/mwgui/journalwindow.hpp b/apps/openmw/mwgui/journalwindow.hpp index c9bf0ef0a..62080f72e 100644 --- a/apps/openmw/mwgui/journalwindow.hpp +++ b/apps/openmw/mwgui/journalwindow.hpp @@ -3,6 +3,8 @@ #include "windowbase.hpp" +#include + #include namespace MWBase { class WindowManager; } @@ -16,7 +18,7 @@ namespace MWGui JournalWindow(); /// construct a new instance of the one JournalWindow implementation - static JournalWindow * create (std::shared_ptr Model, bool questList); + static JournalWindow * create (std::shared_ptr Model, bool questList, ToUTF8::FromType encoding); /// destroy this instance of the JournalWindow implementation virtual ~JournalWindow () {}; diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index f7009d0df..7142a5ce1 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -204,6 +204,7 @@ namespace MWGui , mRestAllowed(true) , mFallbackMap(fallbackMap) , mShowOwned(0) + , mEncoding(encoding) , mVersionDescription(versionDescription) { float uiScale = Settings::Manager::getFloat("scaling factor", "GUI"); @@ -357,7 +358,7 @@ namespace MWGui mGuiModeStates[GM_Console] = GuiModeState(mConsole); bool questList = mResourceSystem->getVFS()->exists("textures/tx_menubook_options_over.dds"); - JournalWindow* journal = JournalWindow::create(JournalViewModel::create (), questList); + JournalWindow* journal = JournalWindow::create(JournalViewModel::create (), questList, mEncoding); mWindows.push_back(journal); mGuiModeStates[GM_Journal] = GuiModeState(journal); mGuiModeStates[GM_Journal].mCloseSound = "book close"; @@ -2115,5 +2116,4 @@ namespace MWGui for (unsigned int i=0; isetVisible(visible); } - } diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 453d55fc3..f84e924aa 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -531,6 +531,8 @@ namespace MWGui int mShowOwned; + ToUTF8::FromType mEncoding; + std::string mVersionDescription; MWGui::TextColours mTextColours; diff --git a/apps/openmw/mwworld/projectilemanager.cpp b/apps/openmw/mwworld/projectilemanager.cpp index ede8c34c4..a4a22ea4a 100644 --- a/apps/openmw/mwworld/projectilemanager.cpp +++ b/apps/openmw/mwworld/projectilemanager.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include @@ -202,6 +203,12 @@ namespace MWWorld osg::ref_ptr projectile = mResourceSystem->getSceneManager()->getInstance(model, attachTo); + osg::ref_ptr boundVisitor = new osg::ComputeBoundsVisitor(); + projectile->accept(*boundVisitor.get()); + osg::BoundingBox bb = boundVisitor->getBoundingBox(); + + state.mNode->setPivotPoint(bb.center()); + if (state.mIdMagic.size() > 1) for (size_t iter = 1; iter != state.mIdMagic.size(); ++iter) { @@ -316,6 +323,7 @@ namespace MWWorld state.mIdArrow = projectile.getCellRef().getRefId(); state.mCasterHandle = actor; state.mAttackStrength = attackStrength; + state.mThrown = projectile.get()->mBase->mData.mType == ESM::Weapon::MarksmanThrown; MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), projectile.getCellRef().getRefId()); MWWorld::Ptr ptr = ref.getPtr(); @@ -378,7 +386,6 @@ namespace MWWorld static float fTargetSpellMaxSpeed = MWBase::Environment::get().getWorld()->getStore().get() .find("fTargetSpellMaxSpeed")->getFloat(); float speed = fTargetSpellMaxSpeed * it->mSpeed; - osg::Vec3f direction = orient * osg::Vec3f(0,1,0); direction.normalize(); osg::Vec3f pos(it->mNode->getPosition()); @@ -458,7 +465,22 @@ namespace MWWorld osg::Vec3f newPos = pos + it->mVelocity * duration; osg::Quat orient; - orient.makeRotate(osg::Vec3f(0,1,0), it->mVelocity); + + if (it->mThrown) + orient.set( + osg::Matrixd::rotate(it->mEffectAnimationTime->getTime() * -10.0,osg::Vec3f(0,0,1)) * + osg::Matrixd::rotate(osg::PI / 2.0,osg::Vec3f(0,1,0)) * + osg::Matrixd::rotate(-1 * osg::PI / 2.0,osg::Vec3f(1,0,0)) * + osg::Matrixd::inverse( + osg::Matrixd::lookAt( + osg::Vec3f(0,0,0), + it->mVelocity, + osg::Vec3f(0,0,1)) + ) + ); + else + orient.makeRotate(osg::Vec3f(0,1,0), it->mVelocity); + it->mNode->setAttitude(orient); it->mNode->setPosition(newPos); @@ -596,6 +618,7 @@ namespace MWWorld MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), esm.mId); MWWorld::Ptr ptr = ref.getPtr(); model = ptr.getClass().getModel(ptr); + state.mThrown = ptr.get()->mBase->mData.mType == ESM::Weapon::MarksmanThrown; } catch(...) { diff --git a/apps/openmw/mwworld/projectilemanager.hpp b/apps/openmw/mwworld/projectilemanager.hpp index 0dfbf6080..ba2fe7a74 100644 --- a/apps/openmw/mwworld/projectilemanager.hpp +++ b/apps/openmw/mwworld/projectilemanager.hpp @@ -112,6 +112,7 @@ namespace MWWorld osg::Vec3f mVelocity; float mAttackStrength; + bool mThrown; }; std::vector mMagicBolts; diff --git a/components/misc/utf8stream.hpp b/components/misc/utf8stream.hpp index 760015902..368374a64 100644 --- a/components/misc/utf8stream.hpp +++ b/components/misc/utf8stream.hpp @@ -18,6 +18,11 @@ public: { } + Utf8Stream (const char * str) : + cur ((unsigned char*) str), nxt ((unsigned char*) str), end ((unsigned char*) str + strlen(str)), val(Utf8Stream::sBadChar()) + { + } + Utf8Stream (std::pair range) : cur (range.first), nxt (range.first), end (range.second), val(Utf8Stream::sBadChar()) {