diff --git a/assets/opensb/_metadata b/assets/opensb/_metadata new file mode 100644 index 0000000..6278360 --- /dev/null +++ b/assets/opensb/_metadata @@ -0,0 +1,7 @@ +{ + "author" : "Not Chucklefish", + "name" : "opensb_base", + "priority" : -9999, + "friendlyName" : "OpenStarbound Assets", + "requires" : ["base"] +} \ No newline at end of file diff --git a/assets/opensb/interface/chat/chat.config.patch b/assets/opensb/interface/chat/chat.config.patch new file mode 100644 index 0000000..9c69e4a --- /dev/null +++ b/assets/opensb/interface/chat/chat.config.patch @@ -0,0 +1,17 @@ +{ + "config" : { + "font" : { + "directives" : "", + "padding" : [0, 0] // Padding to prevent border clipping at the edges of the log canvas while keeping compatible with mods that patch the canvas size + }, + "colors" : { + "local" : "^white;", + "party" : "^blue;", + "broadcast" : "^yellow;", + "whisper" : "^pink;", + "commandResult" : "^lightgray;", + "radioMessage" : "^cyan;", + "world" : "^cyan;" + } + } +} diff --git a/doc/lua/widget.md b/doc/lua/widget.md index 1b29678..246a063 100644 --- a/doc/lua/widget.md +++ b/doc/lua/widget.md @@ -377,7 +377,7 @@ Draws a polygon on the canvas. Draws a list of filled triangles to the canvas. -##### `void` drawText(`String` text, `Json` textPositioning, `unsigned` fontSize, [`Color` color]) +##### `void` drawText(`String` text, `Json` textPositioning, `unsigned` fontSize, [`Color` color], [`float` lineSpacing], [`String` directives]) Draws text on the canvas. textPositioning is in the format: diff --git a/source/base/StarAssets.cpp b/source/base/StarAssets.cpp index 5066268..aab4468 100644 --- a/source/base/StarAssets.cpp +++ b/source/base/StarAssets.cpp @@ -827,29 +827,36 @@ Json Assets::readJson(String const& path) const { Json result = inputUtf8Json(streamData.begin(), streamData.end(), false); for (auto const& pair : m_files.get(path).patchSources) { auto patchStream = pair.second->read(pair.first); - auto patchData = inputUtf8Json(patchStream.begin(), patchStream.end(), false).toArray(); - try { - if (patchData.size()) { - if (patchData.at(0).type() == Json::Type::Array) { - for (auto const& patch : patchData) { + auto patchJson = inputUtf8Json(patchStream.begin(), patchStream.end(), false); + if (patchJson.isType(Json::Type::Array)) { + auto patchData = patchJson.toArray(); + try { + if (patchData.size()) { + if (patchData.at(0).type() == Json::Type::Array) { + for (auto const& patch : patchData) { + try { + result = jsonPatch(result, patch.toArray()); + } catch (JsonPatchTestFail const& e) { + Logger::debug("Patch test failure from file %s in source: %s. Caused by: %s", pair.first, m_assetSourcePaths.getLeft(pair.second), e.what()); + } + } + } else if (patchData.at(0).type() == Json::Type::Object) { try { - result = jsonPatch(result, patch.toArray()); + result = jsonPatch(result, patchData); } catch (JsonPatchTestFail const& e) { Logger::debug("Patch test failure from file %s in source: %s. Caused by: %s", pair.first, m_assetSourcePaths.getLeft(pair.second), e.what()); } + } else { + throw JsonPatchException(strf("Patch data is wrong type: %s", Json::typeName(patchData.at(0).type()))); } - } else if (patchData.at(0).type() == Json::Type::Object) { - try { - result = jsonPatch(result, patchData); - } catch (JsonPatchTestFail const& e) { - Logger::debug("Patch test failure from file %s in source: %s. Caused by: %s", pair.first, m_assetSourcePaths.getLeft(pair.second), e.what()); - } - } else { - throw JsonPatchException(strf("Patch data is wrong type: %s", Json::typeName(patchData.at(0).type()))); } + } catch (JsonPatchException const& e) { + Logger::error("Could not apply patch from file %s in source: %s. Caused by: %s", pair.first, m_assetSourcePaths.getLeft(pair.second), e.what()); } - } catch (JsonPatchException const& e) { - Logger::error("Could not apply patch from file %s in source: %s. Caused by: %s", pair.first, m_assetSourcePaths.getLeft(pair.second), e.what()); + } + else if (patchJson.isType(Json::Type::Object)) { //Kae: Do a good ol' json merge instead if the .patch file is a Json object + auto patchData = patchJson.toObject(); + result = jsonMerge(result, patchData); } } return result; diff --git a/source/frontend/StarChat.cpp b/source/frontend/StarChat.cpp index b444ce4..b89bd4e 100644 --- a/source/frontend/StarChat.cpp +++ b/source/frontend/StarChat.cpp @@ -21,7 +21,9 @@ Chat::Chat(UniverseClientPtr client) : m_client(client) { auto assets = Root::singleton().assets(); m_timeChatLastActive = Time::monotonicMilliseconds(); - m_fontSize = assets->json("/interface/chat/chat.config:config.font.baseSize").toInt(); + auto fontConfig = assets->json("/interface/chat/chat.config:config.font"); + m_fontSize = fontConfig.getInt("baseSize"); + m_fontDirectives = fontConfig.queryString("directives", ""); m_chatLineHeight = assets->json("/interface/chat/chat.config:config.lineHeight").toFloat(); m_chatVisTime = assets->json("/interface/chat/chat.config:config.visTime").toFloat(); m_fadeRate = assets->json("/interface/chat/chat.config:config.fadeRate").toDouble(); @@ -69,6 +71,13 @@ Chat::Chat(UniverseClientPtr client) : m_client(client) { m_say = fetchChild("say"); m_chatLog = fetchChild("chatLog"); + if (auto logPadding = fontConfig.optQuery("padding")) { + m_chatLogPadding = jsonToVec2I(logPadding.get()); + m_chatLog->setSize(m_chatLog->size() + m_chatLogPadding * 2); + m_chatLog->setPosition(m_chatLog->position() - m_chatLogPadding); + } + else + m_chatLogPadding = Vec2I(); m_bottomButton = fetchChild("bottomButton"); m_upButton = fetchChild("upButton"); @@ -224,7 +233,7 @@ void Chat::renderImpl() { m_say->setColor(fadeGreen); m_chatLog->clear(); - Vec2I chatMin; + Vec2I chatMin = m_chatLogPadding; int messageIndex = -m_historyOffset; GuiContext& guiContext = GuiContext::singleton(); @@ -249,7 +258,7 @@ void Chat::renderImpl() { float messageHeight = 0; float lineHeightMargin = + ((m_chatLineHeight * m_fontSize) - m_fontSize); - unsigned wrapWidth = m_chatLog->size()[0]; + unsigned wrapWidth = m_chatLog->size()[0] - m_chatLogPadding[0]; if (message.portrait != "") { TextPositioning tp = {Vec2F(chatMin + m_portraitTextOffset), HorizontalAnchor::LeftAnchor, VerticalAnchor::VMidAnchor, (wrapWidth - m_portraitTextOffset[0])}; @@ -262,13 +271,13 @@ void Chat::renderImpl() { m_chatLog->drawImage(m_portraitBackground, Vec2F(imagePosition), 1.0f, fade); m_chatLog->drawImage(message.portrait, Vec2F(imagePosition + m_portraitImageOffset), m_portraitScale, fade); tp.pos += Vec2F(0, floor(messageHeight / 2)); - m_chatLog->drawText(messageString, tp, m_fontSize, fade, FontMode::Normal, m_chatLineHeight); + m_chatLog->drawText(messageString, tp, m_fontSize, fade, FontMode::Normal, m_chatLineHeight, m_fontDirectives); } else { TextPositioning tp = {Vec2F(chatMin), HorizontalAnchor::LeftAnchor, VerticalAnchor::BottomAnchor, wrapWidth}; messageHeight = guiContext.determineInterfaceTextSize(messageString, tp).size()[1] + lineHeightMargin; - m_chatLog->drawText(messageString, tp, m_fontSize, fade, FontMode::Normal, m_chatLineHeight); + m_chatLog->drawText(messageString, tp, m_fontSize, fade, FontMode::Normal, m_chatLineHeight, m_fontDirectives); } chatMin[1] += ceil(messageHeight); diff --git a/source/frontend/StarChat.hpp b/source/frontend/StarChat.hpp index 4c0dae9..9c1f8f4 100644 --- a/source/frontend/StarChat.hpp +++ b/source/frontend/StarChat.hpp @@ -68,11 +68,13 @@ private: float m_chatVisTime; float m_fadeRate; unsigned m_fontSize; + String m_fontDirectives; float m_chatLineHeight; unsigned m_chatHistoryLimit; int m_historyOffset; CanvasWidgetPtr m_chatLog; + Vec2I m_chatLogPadding; ImageStretchWidgetPtr m_background; int m_defaultHeight; diff --git a/source/frontend/StarWidgetLuaBindings.cpp b/source/frontend/StarWidgetLuaBindings.cpp index f9a8af5..eb81f51 100644 --- a/source/frontend/StarWidgetLuaBindings.cpp +++ b/source/frontend/StarWidgetLuaBindings.cpp @@ -78,8 +78,8 @@ struct LuaUserDataMethods { canvasWidget->drawTriangles(tris, color.value(Color::White).toRgba()); }); methods.registerMethod("drawText", - [](CanvasWidgetPtr canvasWidget, String text, Json tp, unsigned fontSize, Maybe color) { - canvasWidget->drawText(text, TextPositioning(tp), fontSize, color.value(Color::White).toRgba()); + [](CanvasWidgetPtr canvasWidget, String text, Json tp, unsigned fontSize, Maybe color, Maybe lineSpacing, Maybe directives) { + canvasWidget->drawText(text, TextPositioning(tp), fontSize, color.value(Color::White).toRgba(), FontMode::Normal, lineSpacing.value(DefaultLineSpacing), directives.value("")); }); return methods; diff --git a/source/rendering/StarFontTextureGroup.cpp b/source/rendering/StarFontTextureGroup.cpp index 038b535..7517c8b 100644 --- a/source/rendering/StarFontTextureGroup.cpp +++ b/source/rendering/StarFontTextureGroup.cpp @@ -12,24 +12,34 @@ void FontTextureGroup::cleanup(int64_t timeout) { eraseWhere(m_glyphs, [&](auto const& p) { return currentTime - p.second.time > timeout; }); } -TexturePtr FontTextureGroup::glyphTexture(String::Char c, unsigned size) { - return glyphTexture(c, size, ""); -} - -TexturePtr FontTextureGroup::glyphTexture(String::Char c, unsigned size, String const& processingDirectives) { +const FontTextureGroup::GlyphTexture& FontTextureGroup::glyphTexture(String::Char c, unsigned size, String const& processingDirectives) +{ auto res = m_glyphs.insert(GlyphDescriptor{c, size, processingDirectives}, GlyphTexture()); if (res.second) { m_font->setPixelSize(size); Image image = m_font->render(c); - if (!processingDirectives.empty()) + if (!processingDirectives.empty()) { + Vec2F preSize = Vec2F(image.size()); image = processImageOperations(parseImageOperations(processingDirectives), image); + res.first->second.processingOffset = preSize - Vec2F(image.size()); + } + else + res.first->second.processingOffset = Vec2F(); res.first->second.texture = m_textureGroup->create(image); } res.first->second.time = Time::monotonicMilliseconds(); - return res.first->second.texture; + return res.first->second; +} + +TexturePtr FontTextureGroup::glyphTexturePtr(String::Char c, unsigned size) { + return glyphTexture(c, size, "").texture; +} + +TexturePtr FontTextureGroup::glyphTexturePtr(String::Char c, unsigned size, String const& processingDirectives) { + return glyphTexture(c, size, processingDirectives).texture; } unsigned FontTextureGroup::glyphWidth(String::Char c, unsigned fontSize) { diff --git a/source/rendering/StarFontTextureGroup.hpp b/source/rendering/StarFontTextureGroup.hpp index d933f1e..17c6467 100644 --- a/source/rendering/StarFontTextureGroup.hpp +++ b/source/rendering/StarFontTextureGroup.hpp @@ -11,24 +11,27 @@ STAR_CLASS(FontTextureGroup); class FontTextureGroup { public: + typedef tuple GlyphDescriptor; + + struct GlyphTexture { + TexturePtr texture; + int64_t time; + Vec2F processingOffset; + }; + FontTextureGroup(FontPtr font, TextureGroupPtr textureGroup); - TexturePtr glyphTexture(String::Char, unsigned fontSize); - TexturePtr glyphTexture(String::Char, unsigned fontSize, String const& processingDirectives); + const GlyphTexture& glyphTexture(String::Char, unsigned fontSize, String const& processingDirectives); + + TexturePtr glyphTexturePtr(String::Char, unsigned fontSize); + TexturePtr glyphTexturePtr(String::Char, unsigned fontSize, String const& processingDirectives); unsigned glyphWidth(String::Char c, unsigned fontSize); // Removes glyphs that haven't been used in more than the given time in // milliseconds void cleanup(int64_t timeout); - private: - typedef tuple GlyphDescriptor; - - struct GlyphTexture { - TexturePtr texture; - int64_t time; - }; FontPtr m_font; TextureGroupPtr m_textureGroup; diff --git a/source/rendering/StarTextPainter.cpp b/source/rendering/StarTextPainter.cpp index 0ef374d..205aefe 100644 --- a/source/rendering/StarTextPainter.cpp +++ b/source/rendering/StarTextPainter.cpp @@ -379,9 +379,16 @@ RectF TextPainter::doRenderGlyph(String::Char c, TextPositioning const& position if (reallyRender) { if ((int)m_renderSettings.mode & (int)FontMode::Shadow) { Color shadow = Color::Black; - shadow.setAlpha(m_renderSettings.color[3]); + uint8_t alphaU = m_renderSettings.color[3]; + if (alphaU != 255) { + float alpha = byteToFloat(alphaU); + shadow.setAlpha(floatToByte(alpha * (1.5f - 0.5f * alpha))); + } + else + shadow.setAlpha(alphaU); + + //Kae: Draw only one shadow glyph instead of stacking two, alpha modified to appear perceptually the same as vanilla renderGlyph(c, position.pos + Vec2F(hOffset, vOffset - 2), m_fontSize, 1, shadow.toRgba(), m_processingDirectives); - renderGlyph(c, position.pos + Vec2F(hOffset, vOffset - 1), m_fontSize, 1, shadow.toRgba(), m_processingDirectives); } renderGlyph(c, position.pos + Vec2F(hOffset, vOffset), m_fontSize, 1, m_renderSettings.color, m_processingDirectives); @@ -395,8 +402,9 @@ void TextPainter::renderGlyph(String::Char c, Vec2F const& screenPos, unsigned f if (!fontSize) return; - auto texture = m_fontTextureGroup.glyphTexture(c, fontSize, processingDirectives); - m_renderer->render(renderTexturedRect(move(texture), Vec2F(screenPos), scale, color, 0.0f)); + const FontTextureGroup::GlyphTexture& glyphTexture = m_fontTextureGroup.glyphTexture(c, fontSize, processingDirectives); + Vec2F offset = glyphTexture.processingOffset * (scale * 0.5f); //Kae: Re-center the glyph if the image scale was changed by the directives (it is positioned from the bottom left) + m_renderer->render(renderTexturedRect(glyphTexture.texture, Vec2F(screenPos) + offset, scale, color, 0.0f)); } } diff --git a/source/windowing/StarCanvasWidget.cpp b/source/windowing/StarCanvasWidget.cpp index 3e58802..d0c6999 100644 --- a/source/windowing/StarCanvasWidget.cpp +++ b/source/windowing/StarCanvasWidget.cpp @@ -60,8 +60,8 @@ void CanvasWidget::drawTriangles(List> const& triangl m_renderOps.append(make_tuple(triangles, color)); } -void CanvasWidget::drawText(String s, TextPositioning position, unsigned fontSize, Vec4B const& color, FontMode mode, float lineSpacing) { - m_renderOps.append(make_tuple(move(s), move(position), fontSize, color, mode, lineSpacing)); +void CanvasWidget::drawText(String s, TextPositioning position, unsigned fontSize, Vec4B const& color, FontMode mode, float lineSpacing, String processingDirectives) { + m_renderOps.append(make_tuple(move(s), move(position), fontSize, color, mode, lineSpacing, move(processingDirectives))); } Vec2I CanvasWidget::mousePosition() const { @@ -137,7 +137,7 @@ void CanvasWidget::renderImpl() { if (auto args = op.ptr()) tupleUnpackFunction(bind(&CanvasWidget::renderTriangles, this, renderingOffset, _1, _2), *args); if (auto args = op.ptr()) - tupleUnpackFunction(bind(&CanvasWidget::renderText, this, renderingOffset, _1, _2, _3, _4, _5, _6), *args); + tupleUnpackFunction(bind(&CanvasWidget::renderText, this, renderingOffset, _1, _2, _3, _4, _5, _6, _7), *args); } } @@ -222,8 +222,9 @@ void CanvasWidget::renderTriangles(Vec2F const& renderingOffset, List> const& poly, Vec4B const& color = Vec4B(255, 255, 255, 255)); - void drawText(String s, TextPositioning position, unsigned fontSize, Vec4B const& color = Vec4B(255, 255, 255, 255), FontMode mode = FontMode::Normal, float lineSpacing = Star::DefaultLineSpacing); + void drawText(String s, TextPositioning position, unsigned fontSize, Vec4B const& color = Vec4B(255, 255, 255, 255), FontMode mode = FontMode::Normal, float lineSpacing = Star::DefaultLineSpacing, String processingDirectives = ""); protected: void renderImpl() override; @@ -78,7 +78,7 @@ protected: void renderRect(Vec2F const& renderingOffset, RectF const& coords, Vec4B const& color); void renderPoly(Vec2F const& renderingOffset, PolyF poly, Vec4B const& color, float lineWidth); void renderTriangles(Vec2F const& renderingOffset, List> const& triangles, Vec4B const& color); - void renderText(Vec2F const& renderingOffset, String const& s, TextPositioning const& position, unsigned fontSize, Vec4B const& color, FontMode mode, float lineSpacing); + void renderText(Vec2F const& renderingOffset, String const& s, TextPositioning const& position, unsigned fontSize, Vec4B const& color, FontMode mode, float lineSpacing, String const& directives); private: bool m_captureKeyboard; @@ -95,7 +95,7 @@ private: typedef tuple LineOp; typedef tuple PolyOp; typedef tuple>, Vec4B> TrianglesOp; - typedef tuple TextOp; + typedef tuple TextOp; typedef MVariant RenderOp; List m_renderOps;