mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-01-27 03:35:27 +00:00
Allow reading ESM4 books
This commit is contained in:
parent
15e6ababf1
commit
6161953106
@ -4,6 +4,7 @@
|
||||
#include <MyGUI_TextBox.h>
|
||||
|
||||
#include <components/esm3/loadbook.hpp>
|
||||
#include <components/esm4/loadbook.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
@ -83,7 +84,7 @@ namespace MWGui
|
||||
|
||||
void BookWindow::setPtr(const MWWorld::Ptr& book)
|
||||
{
|
||||
if (book.isEmpty() || book.getType() != ESM::REC_BOOK)
|
||||
if (book.isEmpty() || (book.getType() != ESM::REC_BOOK && book.getType() != ESM::REC_BOOK4))
|
||||
throw std::runtime_error("Invalid argument in BookWindow::setPtr");
|
||||
mBook = book;
|
||||
|
||||
@ -93,11 +94,16 @@ namespace MWGui
|
||||
clearPages();
|
||||
mCurrentPage = 0;
|
||||
|
||||
MWWorld::LiveCellRef<ESM::Book>* ref = mBook.get<ESM::Book>();
|
||||
const std::string* text;
|
||||
if (book.getType() == ESM::REC_BOOK)
|
||||
text = &book.get<ESM::Book>()->mBase->mText;
|
||||
else
|
||||
text = &book.get<ESM4::Book>()->mBase->mText;
|
||||
bool shrinkTextAtLastTag = book.getType() == ESM::REC_BOOK;
|
||||
|
||||
Formatting::BookFormatter formatter;
|
||||
mPages = formatter.markupToWidget(mLeftPage, ref->mBase->mText);
|
||||
formatter.markupToWidget(mRightPage, ref->mBase->mText);
|
||||
mPages = formatter.markupToWidget(mLeftPage, *text, shrinkTextAtLastTag);
|
||||
formatter.markupToWidget(mRightPage, *text, shrinkTextAtLastTag);
|
||||
|
||||
updatePages();
|
||||
|
||||
|
@ -23,7 +23,7 @@
|
||||
namespace MWGui::Formatting
|
||||
{
|
||||
/* BookTextParser */
|
||||
BookTextParser::BookTextParser(const std::string& text)
|
||||
BookTextParser::BookTextParser(const std::string& text, bool shrinkTextAtLastTag)
|
||||
: mIndex(0)
|
||||
, mText(text)
|
||||
, mIgnoreNewlineTags(true)
|
||||
@ -36,20 +36,25 @@ namespace MWGui::Formatting
|
||||
|
||||
Misc::StringUtils::replaceAll(mText, "\r", {});
|
||||
|
||||
// vanilla game does not show any text after the last EOL tag.
|
||||
const std::string lowerText = Misc::StringUtils::lowerCase(mText);
|
||||
size_t brIndex = lowerText.rfind("<br>");
|
||||
size_t pIndex = lowerText.rfind("<p>");
|
||||
mPlainTextEnd = 0;
|
||||
if (brIndex != pIndex)
|
||||
if (shrinkTextAtLastTag)
|
||||
{
|
||||
if (brIndex != std::string::npos && pIndex != std::string::npos)
|
||||
mPlainTextEnd = std::max(brIndex, pIndex);
|
||||
else if (brIndex != std::string::npos)
|
||||
mPlainTextEnd = brIndex;
|
||||
else
|
||||
mPlainTextEnd = pIndex;
|
||||
// vanilla game does not show any text after the last EOL tag.
|
||||
const std::string lowerText = Misc::StringUtils::lowerCase(mText);
|
||||
size_t brIndex = lowerText.rfind("<br>");
|
||||
size_t pIndex = lowerText.rfind("<p>");
|
||||
mPlainTextEnd = 0;
|
||||
if (brIndex != pIndex)
|
||||
{
|
||||
if (brIndex != std::string::npos && pIndex != std::string::npos)
|
||||
mPlainTextEnd = std::max(brIndex, pIndex);
|
||||
else if (brIndex != std::string::npos)
|
||||
mPlainTextEnd = brIndex;
|
||||
else
|
||||
mPlainTextEnd = pIndex;
|
||||
}
|
||||
}
|
||||
else
|
||||
mPlainTextEnd = mText.size();
|
||||
|
||||
registerTag("br", Event_BrTag);
|
||||
registerTag("p", Event_PTag);
|
||||
@ -73,6 +78,17 @@ namespace MWGui::Formatting
|
||||
while (mIndex < mText.size())
|
||||
{
|
||||
char ch = mText[mIndex];
|
||||
if (ch == '[')
|
||||
{
|
||||
constexpr std::string_view pageBreakTag = "[pagebreak]\n";
|
||||
if (std::string_view(mText.data() + mIndex, mText.size() - mIndex).starts_with(pageBreakTag))
|
||||
{
|
||||
mIndex += pageBreakTag.size();
|
||||
flushBuffer();
|
||||
mIgnoreNewlineTags = false;
|
||||
return Event_PageBreak;
|
||||
}
|
||||
}
|
||||
if (ch == '<')
|
||||
{
|
||||
const size_t tagStart = mIndex + 1;
|
||||
@ -98,6 +114,8 @@ namespace MWGui::Formatting
|
||||
}
|
||||
}
|
||||
mIgnoreLineEndings = true;
|
||||
if (type == Event_PTag && !mAttributes.empty())
|
||||
flushBuffer();
|
||||
}
|
||||
else
|
||||
flushBuffer();
|
||||
@ -180,9 +198,9 @@ namespace MWGui::Formatting
|
||||
if (tag.empty())
|
||||
return;
|
||||
|
||||
if (tag[0] == '"')
|
||||
if (tag[0] == '"' || tag[0] == '\'')
|
||||
{
|
||||
size_t quoteEndPos = tag.find('"', 1);
|
||||
size_t quoteEndPos = tag.find(tag[0], 1);
|
||||
if (quoteEndPos == std::string::npos)
|
||||
throw std::runtime_error("BookTextParser Error: Missing end quote in tag");
|
||||
value = tag.substr(1, quoteEndPos - 1);
|
||||
@ -208,8 +226,8 @@ namespace MWGui::Formatting
|
||||
}
|
||||
|
||||
/* BookFormatter */
|
||||
Paginator::Pages BookFormatter::markupToWidget(
|
||||
MyGUI::Widget* parent, const std::string& markup, const int pageWidth, const int pageHeight)
|
||||
Paginator::Pages BookFormatter::markupToWidget(MyGUI::Widget* parent, const std::string& markup,
|
||||
const int pageWidth, const int pageHeight, bool shrinkTextAtLastTag)
|
||||
{
|
||||
Paginator pag(pageWidth, pageHeight);
|
||||
|
||||
@ -225,14 +243,16 @@ namespace MWGui::Formatting
|
||||
MyGUI::IntCoord(0, 0, pag.getPageWidth(), pag.getPageHeight()), MyGUI::Align::Left | MyGUI::Align::Top);
|
||||
paper->setNeedMouseFocus(false);
|
||||
|
||||
BookTextParser parser(markup);
|
||||
BookTextParser parser(markup, shrinkTextAtLastTag);
|
||||
|
||||
bool brBeforeLastTag = false;
|
||||
bool isPrevImg = false;
|
||||
bool inlineImageInserted = false;
|
||||
for (;;)
|
||||
{
|
||||
BookTextParser::Events event = parser.next();
|
||||
if (event == BookTextParser::Event_BrTag || event == BookTextParser::Event_PTag)
|
||||
if (event == BookTextParser::Event_BrTag
|
||||
|| (event == BookTextParser::Event_PTag && parser.getAttributes().empty()))
|
||||
continue;
|
||||
|
||||
std::string plainText = parser.getReadyText();
|
||||
@ -272,6 +292,12 @@ namespace MWGui::Formatting
|
||||
|
||||
if (!plainText.empty() || brBeforeLastTag || isPrevImg)
|
||||
{
|
||||
if (inlineImageInserted)
|
||||
{
|
||||
pag.setCurrentTop(pag.getCurrentTop() - mTextStyle.mTextSize);
|
||||
plainText = " " + plainText;
|
||||
inlineImageInserted = false;
|
||||
}
|
||||
TextElement elem(paper, pag, mBlockStyle, mTextStyle, plainText);
|
||||
elem.paginate();
|
||||
}
|
||||
@ -286,6 +312,10 @@ namespace MWGui::Formatting
|
||||
|
||||
switch (event)
|
||||
{
|
||||
case BookTextParser::Event_PageBreak:
|
||||
pag << Paginator::Page(pag.getStartTop(), pag.getCurrentTop());
|
||||
pag.setStartTop(pag.getCurrentTop());
|
||||
break;
|
||||
case BookTextParser::Event_ImgTag:
|
||||
{
|
||||
const BookTextParser::Attributes& attr = parser.getAttributes();
|
||||
@ -293,22 +323,38 @@ namespace MWGui::Formatting
|
||||
auto srcIt = attr.find("src");
|
||||
if (srcIt == attr.end())
|
||||
continue;
|
||||
auto widthIt = attr.find("width");
|
||||
if (widthIt == attr.end())
|
||||
continue;
|
||||
auto heightIt = attr.find("height");
|
||||
if (heightIt == attr.end())
|
||||
continue;
|
||||
int width = 0;
|
||||
if (auto widthIt = attr.find("width"); widthIt != attr.end())
|
||||
width = MyGUI::utility::parseInt(widthIt->second);
|
||||
int height = 0;
|
||||
if (auto heightIt = attr.find("height"); heightIt != attr.end())
|
||||
height = MyGUI::utility::parseInt(heightIt->second);
|
||||
|
||||
const std::string& src = srcIt->second;
|
||||
int width = MyGUI::utility::parseInt(widthIt->second);
|
||||
int height = MyGUI::utility::parseInt(heightIt->second);
|
||||
|
||||
auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS();
|
||||
std::string correctedSrc = Misc::ResourceHelpers::correctBookartPath(src, width, height, vfs);
|
||||
bool exists = vfs->exists(correctedSrc);
|
||||
|
||||
if (!exists)
|
||||
std::string correctedSrc;
|
||||
|
||||
constexpr std::string_view imgPrefix = "img://";
|
||||
if (src.starts_with(imgPrefix))
|
||||
{
|
||||
correctedSrc = src.substr(imgPrefix.size(), src.size() - imgPrefix.size());
|
||||
if (width == 0)
|
||||
{
|
||||
width = 50;
|
||||
inlineImageInserted = true;
|
||||
}
|
||||
if (height == 0)
|
||||
height = 50;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (width == 0 || height == 0)
|
||||
continue;
|
||||
correctedSrc = Misc::ResourceHelpers::correctBookartPath(src, width, height, vfs);
|
||||
}
|
||||
|
||||
if (!vfs->exists(correctedSrc))
|
||||
{
|
||||
Log(Debug::Warning) << "Warning: Could not find \"" << src << "\" referenced by an <img> tag.";
|
||||
break;
|
||||
@ -326,6 +372,7 @@ namespace MWGui::Formatting
|
||||
else
|
||||
handleFont(parser.getAttributes());
|
||||
break;
|
||||
case BookTextParser::Event_PTag:
|
||||
case BookTextParser::Event_DivTag:
|
||||
handleDiv(parser.getAttributes());
|
||||
break;
|
||||
@ -343,9 +390,10 @@ namespace MWGui::Formatting
|
||||
return pag.getPages();
|
||||
}
|
||||
|
||||
Paginator::Pages BookFormatter::markupToWidget(MyGUI::Widget* parent, const std::string& markup)
|
||||
Paginator::Pages BookFormatter::markupToWidget(
|
||||
MyGUI::Widget* parent, const std::string& markup, bool shrinkTextAtLastTag)
|
||||
{
|
||||
return markupToWidget(parent, markup, parent->getWidth(), parent->getHeight());
|
||||
return markupToWidget(parent, markup, parent->getWidth(), parent->getHeight(), shrinkTextAtLastTag);
|
||||
}
|
||||
|
||||
void BookFormatter::resetFontProperties()
|
||||
|
@ -46,10 +46,11 @@ namespace MWGui
|
||||
Event_PTag,
|
||||
Event_ImgTag,
|
||||
Event_DivTag,
|
||||
Event_FontTag
|
||||
Event_FontTag,
|
||||
Event_PageBreak,
|
||||
};
|
||||
|
||||
BookTextParser(const std::string& text);
|
||||
BookTextParser(const std::string& text, bool shrinkTextAtLastTag);
|
||||
|
||||
Events next();
|
||||
|
||||
@ -120,9 +121,9 @@ namespace MWGui
|
||||
class BookFormatter
|
||||
{
|
||||
public:
|
||||
Paginator::Pages markupToWidget(
|
||||
MyGUI::Widget* parent, const std::string& markup, const int pageWidth, const int pageHeight);
|
||||
Paginator::Pages markupToWidget(MyGUI::Widget* parent, const std::string& markup);
|
||||
Paginator::Pages markupToWidget(MyGUI::Widget* parent, const std::string& markup, const int pageWidth,
|
||||
const int pageHeight, bool shrinkTextAtLastTag);
|
||||
Paginator::Pages markupToWidget(MyGUI::Widget* parent, const std::string& markup, bool shrinkTextAtLastTag);
|
||||
|
||||
private:
|
||||
void resetFontProperties();
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include <MyGUI_ScrollView.h>
|
||||
|
||||
#include <components/esm3/loadbook.hpp>
|
||||
#include <components/esm4/loadbook.hpp>
|
||||
#include <components/widgets/imagebutton.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
@ -42,17 +43,22 @@ namespace MWGui
|
||||
|
||||
void ScrollWindow::setPtr(const MWWorld::Ptr& scroll)
|
||||
{
|
||||
if (scroll.isEmpty() || scroll.getType() != ESM::REC_BOOK)
|
||||
if (scroll.isEmpty() || (scroll.getType() != ESM::REC_BOOK && scroll.getType() != ESM::REC_BOOK4))
|
||||
throw std::runtime_error("Invalid argument in ScrollWindow::setPtr");
|
||||
mScroll = scroll;
|
||||
|
||||
MWWorld::Ptr player = MWMechanics::getPlayer();
|
||||
bool showTakeButton = scroll.getContainerStore() != &player.getClass().getContainerStore(player);
|
||||
|
||||
MWWorld::LiveCellRef<ESM::Book>* ref = mScroll.get<ESM::Book>();
|
||||
const std::string* text;
|
||||
if (scroll.getType() == ESM::REC_BOOK)
|
||||
text = &scroll.get<ESM::Book>()->mBase->mText;
|
||||
else
|
||||
text = &scroll.get<ESM4::Book>()->mBase->mText;
|
||||
bool shrinkTextAtLastTag = scroll.getType() == ESM::REC_BOOK;
|
||||
|
||||
Formatting::BookFormatter formatter;
|
||||
formatter.markupToWidget(mTextView, ref->mBase->mText, 390, mTextView->getHeight());
|
||||
formatter.markupToWidget(mTextView, *text, 390, mTextView->getHeight(), shrinkTextAtLastTag);
|
||||
MyGUI::IntSize size = mTextView->getChildAt(0)->getSize();
|
||||
|
||||
// Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the
|
||||
|
@ -32,6 +32,8 @@ Example:
|
||||
UI events
|
||||
---------
|
||||
|
||||
**UiModeChanged**
|
||||
|
||||
Every time UI mode is changed built-in scripts send to player the event ``UiModeChanged`` with arguments ``oldMode, ``newMode`` (same as ``I.UI.getMode()``)
|
||||
and ``arg`` (for example in the mode ``Book`` the argument is the book the player is reading).
|
||||
|
||||
@ -43,6 +45,22 @@ and ``arg`` (for example in the mode ``Book`` the argument is the book the playe
|
||||
end
|
||||
}
|
||||
|
||||
**AddUiMode**
|
||||
|
||||
Equivalent to ``I.UI.addMode``, but can be sent from another object or global script.
|
||||
|
||||
.. code-block:: Lua
|
||||
|
||||
player:sendEvent('AddUiMode', {mode = 'Book', target = book})
|
||||
|
||||
**SetUiMode**
|
||||
|
||||
Equivalent to ``I.UI.setMode``, but can be sent from another object or global script.
|
||||
|
||||
.. code-block:: Lua
|
||||
|
||||
player:sendEvent('SetUiMode', {mode = 'Book', target = book})
|
||||
|
||||
World events
|
||||
------------
|
||||
|
||||
|
@ -459,7 +459,8 @@ Using the interface:
|
||||
|
||||
The order in which the scripts are started is important. So if one mod should override an interface provided by another mod, make sure that load order (i.e. the sequence of `lua-scripts=...` in `openmw.cfg`) is correct.
|
||||
|
||||
**Interfaces of built-in scripts**
|
||||
Interfaces of built-in scripts
|
||||
------------------------------
|
||||
|
||||
.. include:: tables/interfaces.rst
|
||||
|
||||
|
@ -17,9 +17,16 @@ local function ESM4DoorActivation(door, actor)
|
||||
return false -- disable activation handling in C++ mwmechanics code
|
||||
end
|
||||
|
||||
local function ESM4BookActivation(book, actor)
|
||||
if actor.type == types.Player then
|
||||
actor:sendEvent('AddUiMode', { mode = 'Book', target = book })
|
||||
end
|
||||
end
|
||||
|
||||
local handlersPerObject = {}
|
||||
local handlersPerType = {}
|
||||
|
||||
handlersPerType[types.ESM4Book] = { ESM4BookActivation }
|
||||
handlersPerType[types.ESM4Door] = { ESM4DoorActivation }
|
||||
|
||||
local function onActivate(obj, actor)
|
||||
|
@ -240,5 +240,7 @@ return {
|
||||
},
|
||||
eventHandlers = {
|
||||
UiModeChanged = onUiModeChangedEvent,
|
||||
AddUiMode = function(options) addMode(options.mode, options) end,
|
||||
SetUiMode = function(options) setMode(options.mode, options) end,
|
||||
},
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user