From 4288589f11538ddf6b314b5d4872a46719f5f11c Mon Sep 17 00:00:00 2001 From: Ted John Date: Sat, 4 Dec 2021 17:41:09 +0000 Subject: [PATCH] Refactor large scenery to use ImageId --- src/openrct2/paint/Paint.cpp | 7 +- src/openrct2/paint/Paint.h | 2 +- .../paint/tile_element/Paint.LargeScenery.cpp | 684 ++++++++---------- src/openrct2/world/Scenery.cpp | 56 ++ src/openrct2/world/Scenery.h | 22 +- 5 files changed, 381 insertions(+), 390 deletions(-) diff --git a/src/openrct2/paint/Paint.cpp b/src/openrct2/paint/Paint.cpp index 98aa8fc8a5..77222465e6 100644 --- a/src/openrct2/paint/Paint.cpp +++ b/src/openrct2/paint/Paint.cpp @@ -868,12 +868,12 @@ paint_struct* PaintAddImageAsChild( * @param y (cx) * @return (!CF) success */ -bool PaintAttachToPreviousAttach(paint_session* session, uint32_t image_id, int32_t x, int32_t y) +bool PaintAttachToPreviousAttach(paint_session* session, ImageId imageId, int32_t x, int32_t y) { auto* previousAttachedPS = session->LastAttachedPS; if (previousAttachedPS == nullptr) { - return PaintAttachToPreviousPS(session, image_id, x, y); + return PaintAttachToPreviousPS(session, imageId, x, y); } auto* ps = session->AllocateAttachedPaintEntry(); @@ -882,7 +882,8 @@ bool PaintAttachToPreviousAttach(paint_session* session, uint32_t image_id, int3 return false; } - ps->image_id = image_id; + ps->image_id = imageId.ToUInt32(); + ps->tertiary_colour = imageId.GetTertiary(); ps->x = x; ps->y = y; ps->flags = 0; diff --git a/src/openrct2/paint/Paint.h b/src/openrct2/paint/Paint.h index 6d03b1971b..cb7ee44154 100644 --- a/src/openrct2/paint/Paint.h +++ b/src/openrct2/paint/Paint.h @@ -331,7 +331,7 @@ paint_struct* PaintAddImageAsParentRotated( void paint_util_push_tunnel_rotated(paint_session* session, uint8_t direction, uint16_t height, uint8_t type); -bool PaintAttachToPreviousAttach(paint_session* session, uint32_t image_id, int32_t x, int32_t y); +bool PaintAttachToPreviousAttach(paint_session* session, ImageId imageId, int32_t x, int32_t y); bool PaintAttachToPreviousPS(paint_session* session, ImageId image_id, int32_t x, int32_t y); bool PaintAttachToPreviousPS(paint_session* session, uint32_t image_id, int32_t x, int32_t y); void PaintFloatingMoneyEffect( diff --git a/src/openrct2/paint/tile_element/Paint.LargeScenery.cpp b/src/openrct2/paint/tile_element/Paint.LargeScenery.cpp index 1333172a8f..33331b7bb0 100644 --- a/src/openrct2/paint/tile_element/Paint.LargeScenery.cpp +++ b/src/openrct2/paint/tile_element/Paint.LargeScenery.cpp @@ -12,6 +12,7 @@ #include "../../Game.h" #include "../../config/Config.h" #include "../../core/Numerics.hpp" +#include "../../core/String.hpp" #include "../../interface/Viewport.h" #include "../../localisation/Localisation.h" #include "../../object/LargeSceneryObject.h" @@ -26,171 +27,6 @@ #include "../Supports.h" #include "Paint.TileElement.h" -#include - -// 6B8172: -static void large_scenery_paint_supports( - paint_session* session, uint8_t direction, uint16_t height, const LargeSceneryElement& tileElement, uint32_t dword_F4387C, - const rct_large_scenery_tile* tile) -{ - if (tile->flags & LARGE_SCENERY_TILE_FLAG_NO_SUPPORTS) - { - return; - } - - int32_t ax = 0; - int32_t supportHeight = height; - - if (supportHeight & 0xF) - { - supportHeight &= 0xFFFFFFF0; - ax = 49; - } - - int32_t supportImageColourFlags = IMAGE_TYPE_REMAP; - - if (dword_F4387C) - { - supportImageColourFlags = dword_F4387C; - } - - wooden_b_supports_paint_setup(session, (direction & 1), ax, supportHeight, supportImageColourFlags); - - int32_t clearanceHeight = ceil2(tileElement.GetClearanceZ() + 15, 16); - - if (tile->flags & LARGE_SCENERY_TILE_FLAG_ALLOW_SUPPORTS_ABOVE) - { - paint_util_set_segment_support_height(session, SEGMENTS_ALL, clearanceHeight, 0x20); - } - else - { - paint_util_set_segment_support_height(session, SEGMENTS_ALL, 0xFFFF, 0); - } - - paint_util_set_general_support_height(session, clearanceHeight, 0x20); -} - -static rct_large_scenery_text_glyph* large_scenery_sign_get_glyph(LargeSceneryText* text, uint32_t codepoint) -{ - if (codepoint >= std::size(text->glyphs)) - { - return &text->glyphs[static_cast('?')]; - } - return &text->glyphs[codepoint]; -} - -static int32_t large_scenery_sign_text_width(const utf8* str, LargeSceneryText* text) -{ - int32_t width = 0; - uint32_t codepoint; - while ((codepoint = utf8_get_next(str, &str)) != 0) - { - width += large_scenery_sign_get_glyph(text, codepoint)->width; - } - return width; -} - -static int32_t large_scenery_sign_text_height(const utf8* str, LargeSceneryText* text) -{ - int32_t height = 0; - uint32_t codepoint; - while ((codepoint = utf8_get_next(str, &str)) != 0) - { - height += large_scenery_sign_get_glyph(text, codepoint)->height; - } - return height; -} - -static void large_scenery_sign_fit_text(const utf8* str, LargeSceneryText* text, bool height, utf8* fitStr, size_t bufLen) -{ - utf8* fitStrEnd = fitStr; - safe_strcpy(fitStr, str, bufLen); - int32_t w = 0; - uint32_t codepoint; - while (w <= text->max_width && (codepoint = utf8_get_next(fitStrEnd, const_cast(&fitStrEnd))) != 0) - { - if (height) - { - w += large_scenery_sign_get_glyph(text, codepoint)->height; - } - else - { - w += large_scenery_sign_get_glyph(text, codepoint)->width; - } - } - *fitStrEnd = 0; -} - -static int32_t div_to_minus_infinity(int32_t a, int32_t b) -{ - return (a / b) - (a % b < 0); -} - -static void large_scenery_sign_paint_line( - paint_session* session, const utf8* str, LargeSceneryText* text, int32_t textImage, int32_t textColour, uint8_t direction, - int32_t y_offset) -{ - utf8 fitStr[32]; - large_scenery_sign_fit_text(str, text, false, fitStr, sizeof(fitStr)); - int32_t width = large_scenery_sign_text_width(fitStr, text); - int32_t x_offset = text->offset[(direction & 1)].x; - int32_t acc = y_offset * ((direction & 1) ? -1 : 1); - if (!(text->flags & LARGE_SCENERY_TEXT_FLAG_VERTICAL)) - { - // sign is horizontal, centre text: - x_offset -= (width / 2); - acc -= (width / 2); - } - uint32_t codepoint; - const utf8* fitStrPtr = fitStr; - while ((codepoint = utf8_get_next(fitStrPtr, &fitStrPtr)) != 0) - { - int32_t glyph_offset = large_scenery_sign_get_glyph(text, codepoint)->image_offset; - uint8_t glyph_type = direction & 1; - if (text->flags & LARGE_SCENERY_TEXT_FLAG_VERTICAL) - { // vertical sign - glyph_offset *= 2; - } - else - { - glyph_offset *= 4; - // set slightly different glyph on horizontal sign, which was rendered 1/2 pixel lower to deal with aliasing: - if (direction & 1) - { - if (!(acc & 1)) - { - glyph_type += 2; - } - } - else - { - if ((acc & 1)) - { - glyph_type += 2; - } - } - } - int32_t image_id = (textImage + glyph_offset + glyph_type) | textColour; - if (direction == 3) - { - PaintAttachToPreviousPS(session, image_id, x_offset, -div_to_minus_infinity(acc, 2)); - } - else - { - if (text->flags & LARGE_SCENERY_TEXT_FLAG_VERTICAL) - { - PaintAttachToPreviousPS(session, image_id, x_offset, div_to_minus_infinity(acc, 2)); - } - else - { - PaintAttachToPreviousAttach(session, image_id, x_offset, div_to_minus_infinity(acc, 2)); - } - } - x_offset += large_scenery_sign_get_glyph(text, codepoint)->width; - acc += large_scenery_sign_get_glyph(text, codepoint)->width; - } -} - struct boundbox { CoordsXY offset; @@ -198,7 +34,7 @@ struct boundbox }; // clang-format off -static constexpr const boundbox s98E3C4[] = { +static constexpr const boundbox LargeSceneryBoundBoxes[] = { { { 3, 3 }, { 26, 26 } }, { { 17, 17 }, { 12, 12 } }, { { 17, 3 }, { 12, 12 } }, @@ -219,19 +55,279 @@ static constexpr const boundbox s98E3C4[] = { }; // clang-format on -/* - * - * rct2: 0x006B7F0C - */ +static void PaintLargeScenerySupports( + paint_session* session, uint8_t direction, uint16_t height, const LargeSceneryElement& tileElement, ImageId imageTemplate, + const rct_large_scenery_tile& tile) +{ + if (tile.flags & LARGE_SCENERY_TILE_FLAG_NO_SUPPORTS) + return; + + auto special = 0; + auto supportHeight = height; + if (supportHeight & 0xF) + { + supportHeight &= ~0xF; + special = 49; + } + + wooden_b_supports_paint_setup(session, (direction & 1), special, supportHeight, imageTemplate.ToUInt32()); + + int32_t clearanceHeight = ceil2(tileElement.GetClearanceZ() + 15, 16); + if (tile.flags & LARGE_SCENERY_TILE_FLAG_ALLOW_SUPPORTS_ABOVE) + { + paint_util_set_segment_support_height(session, SEGMENTS_ALL, clearanceHeight, 0x20); + } + else + { + paint_util_set_segment_support_height(session, SEGMENTS_ALL, 0xFFFF, 0); + } + paint_util_set_general_support_height(session, clearanceHeight, 0x20); +} + +static std::string_view LargeSceneryCalculateDisplayText(const LargeSceneryText& text, std::string_view s, bool height) +{ + size_t totalSize = 0; + CodepointView view(s); + auto it = view.begin(); + while (it != view.end() && totalSize <= text.max_width) + { + auto glyph = text.GetGlyph(*it, ' '); + totalSize += height ? glyph.height : glyph.width; + it++; + } + + auto totalLength = it.GetIndex(); + return s.substr(0, totalLength); +} + +static int32_t DivToMinusInfinity(int32_t a, int32_t b) +{ + return (a / b) - (a % b < 0); +} + +static void PaintLargeScenery3DTextLine( + paint_session* session, const LargeSceneryEntry& sceneryEntry, const LargeSceneryText& text, std::string_view line, + ImageId imageTemplate, Direction direction, int32_t offsetY) +{ + line = LargeSceneryCalculateDisplayText(text, line, false); + auto width = text.MeasureWidth(line); + auto offsetX = text.offset[(direction & 1)].x; + auto acc = offsetY * ((direction & 1) ? -1 : 1); + if (!(text.flags & LARGE_SCENERY_TEXT_FLAG_VERTICAL)) + { + // sign is horizontal, centre text: + offsetX -= (width / 2); + acc -= (width / 2); + } + + for (auto codepoint : CodepointView(line)) + { + auto glyph = text.GetGlyph(codepoint, ' '); + auto glyphOffset = glyph.image_offset; + auto glyphType = direction & 1; + if (text.flags & LARGE_SCENERY_TEXT_FLAG_VERTICAL) + { + glyphOffset *= 2; + } + else + { + glyphOffset *= 4; + // set slightly different glyph on horizontal sign, which was rendered 1/2 pixel lower to deal with aliasing: + if (direction & 1) + { + if (!(acc & 1)) + { + glyphType += 2; + } + } + else + { + if ((acc & 1)) + { + glyphType += 2; + } + } + } + + auto imageIndex = sceneryEntry.text_image + glyphOffset + glyphType; + auto imageId = imageTemplate.WithIndex(imageIndex); + if (direction == 3) + { + PaintAttachToPreviousPS(session, imageId, offsetX, -DivToMinusInfinity(acc, 2)); + } + else if (text.flags & LARGE_SCENERY_TEXT_FLAG_VERTICAL) + { + PaintAttachToPreviousPS(session, imageId, offsetX, DivToMinusInfinity(acc, 2)); + } + else + { + PaintAttachToPreviousAttach(session, imageId, offsetX, DivToMinusInfinity(acc, 2)); + } + offsetX += glyph.width; + acc += glyph.width; + } +} + +static bool Is3DTextSingleLine(const LargeSceneryText& text, std::string_view s) +{ + if (text.flags & LARGE_SCENERY_TEXT_FLAG_TWO_LINE) + { + auto width = text.MeasureWidth(s); + return width <= text.max_width; + } + return true; +} + +static void PaintLargeScenery3DText( + paint_session* session, const LargeSceneryEntry& sceneryEntry, const rct_large_scenery_tile& tile, + const LargeSceneryElement& tileElement, uint8_t direction, uint16_t height, bool isGhost) +{ + if (sceneryEntry.tiles[1].x_offset != -1) + { + auto sequenceDirection = (tileElement.GetSequenceIndex() - 1) & 3; + if (sequenceDirection != direction) + { + return; + } + } + + if (session->DPI.zoom_level > ZoomLevel{ 1 }) + return; + + auto banner = tileElement.GetBanner(); + if (banner == nullptr) + return; + + const auto* text = sceneryEntry.text; + if (text == nullptr) + return; + + auto textColour = isGhost ? static_cast(COLOUR_GREY) : tileElement.GetSecondaryColour(); + auto imageTemplate = ImageId().WithPrimary(textColour); + + char signString[256]; + auto ft = Formatter(); + banner->FormatTextTo(ft); + format_string(signString, sizeof(signString), STR_STRINGID, ft.Data()); + + auto offsetY = text->offset[(direction & 1)].y * 2; + if (text->flags & LARGE_SCENERY_TEXT_FLAG_VERTICAL) + { + // Vertical sign + offsetY++; + auto displayText = LargeSceneryCalculateDisplayText(*text, signString, true); + auto displayTextHeight = text->MeasureHeight(displayText); + for (auto codepoint : CodepointView(displayText)) + { + char line[8]{}; + utf8_write_codepoint(line, codepoint); + PaintLargeScenery3DTextLine( + session, sceneryEntry, *text, line, imageTemplate, direction, offsetY - displayTextHeight); + + auto glyph = text->GetGlyph(codepoint, ' '); + offsetY += glyph.height * 2; + } + } + else + { + // Horizontal sign + offsetY -= (direction & 1); + if (Is3DTextSingleLine(*text, signString)) + { + PaintLargeScenery3DTextLine(session, sceneryEntry, *text, signString, imageTemplate, direction, offsetY); + } + else + { + auto lineHeight = text->GetGlyph('A')->height + 1; + offsetY -= lineHeight; + + // Split the string into two lines at best position + auto current = std::string_view(signString); + std::string_view next; + for (int32_t lineIndex = 0; lineIndex < 2; lineIndex++) + { + std::string_view best; + + CodepointView view(current); + auto lineWidth = 0; + auto it = view.begin(); + while (it != view.end() && lineWidth < text->max_width) + { + // Trim any leading spaces + auto codepoint = *it; + if (codepoint != ' ' || lineWidth != 0) + { + // Determine if this is a good place to split + if (codepoint == ' ' || codepoint == '\n') + { + auto index = it.GetIndex(); + best = current.substr(0, index); + next = current.substr(index + 1); + if (codepoint == '\n') + break; + } + + auto glyph = text->GetGlyph(*it, ' '); + lineWidth += glyph.width; + } + it++; + } + + if (best.empty()) + { + // No good split found, or reached end of string + auto index = it.GetIndex(); + best = current.substr(0, index); + next = current.substr(index); + } + + PaintLargeScenery3DTextLine(session, sceneryEntry, *text, best, imageTemplate, direction, offsetY); + + current = next; + offsetY += lineHeight * 2; + } + } + } +} + +static void PaintLargeSceneryScrollingText( + paint_session* session, const LargeSceneryEntry& sceneryEntry, const LargeSceneryElement& tileElement, uint8_t direction, + uint16_t height, const CoordsXYZ& bbOffset, bool isGhost) +{ + auto textColour = isGhost ? static_cast(COLOUR_GREY) : tileElement.GetSecondaryColour(); + auto textPaletteIndex = direction == 0 ? ColourMapA[textColour].mid_dark : ColourMapA[textColour].light; + + auto banner = tileElement.GetBanner(); + if (banner == nullptr) + return; + + auto ft = Formatter(); + banner->FormatTextTo(ft); + + char text[256]; + if (gConfigGeneral.upper_case_banners) + { + format_string_to_upper(text, sizeof(text), STR_SCROLLING_SIGN_TEXT, ft.Data()); + } + else + { + format_string(text, sizeof(text), STR_SCROLLING_SIGN_TEXT, ft.Data()); + } + + auto scrollMode = sceneryEntry.scrolling_mode + ((direction + 1) & 3); + auto stringWidth = gfx_get_string_width(text, FontSpriteBase::TINY); + auto scroll = stringWidth > 0 ? (gCurrentTicks / 2) % stringWidth : 0; + auto imageId = scrolling_text_setup(session, STR_SCROLLING_SIGN_TEXT, ft, scroll, scrollMode, textPaletteIndex); + PaintAddImageAsChild(session, imageId, { 0, 0, height + 25 }, { 1, 1, 21 }, bbOffset); +} + void PaintLargeScenery(paint_session* session, uint8_t direction, uint16_t height, const LargeSceneryElement& tileElement) { if (session->ViewFlags & VIEWPORT_FLAG_HIGHLIGHT_PATH_ISSUES) - { return; - } - session->InteractionType = ViewportInteractionItem::LargeScenery; - const uint32_t sequenceNum = tileElement.GetSequenceIndex(); - const LargeSceneryObject* object = tileElement.GetObject(); + + auto sequenceNum = tileElement.GetSequenceIndex(); + const auto* object = tileElement.GetObject(); if (object == nullptr) return; @@ -239,216 +335,64 @@ void PaintLargeScenery(paint_session* session, uint8_t direction, uint16_t heigh if (sceneryEntry == nullptr) return; - const rct_large_scenery_tile* tile = object->GetTileForSequence(sequenceNum); + const auto* tile = object->GetTileForSequence(sequenceNum); if (tile == nullptr) return; - auto imageId = ImageId( - sceneryEntry->image + 4 + (sequenceNum << 2) + direction, tileElement.GetPrimaryColour(), - tileElement.GetSecondaryColour()); - uint32_t imageFlags = 0; - if (gTrackDesignSaveMode) + session->InteractionType = ViewportInteractionItem::LargeScenery; + + auto isGhost = false; + ImageId imageTemplate; + if (gTrackDesignSaveMode && !track_design_save_contains_tile_element(reinterpret_cast(&tileElement))) { - if (!track_design_save_contains_tile_element(reinterpret_cast(&tileElement))) - { - imageId = imageId.WithRemap(FilterPaletteID::Palette46); - imageFlags = SPRITE_ID_PALETTE_COLOUR_1(EnumValue(FilterPaletteID::Palette46)); - } + imageTemplate = ImageId().WithRemap(FilterPaletteID::Palette46); + isGhost = true; } - if (tileElement.IsGhost()) + else if (tileElement.IsGhost()) { session->InteractionType = ViewportInteractionItem::None; - imageId = imageId.WithRemap(FilterPaletteID::Palette44); - imageFlags = CONSTRUCTION_MARKER; + imageTemplate = ImageId().WithRemap(FilterPaletteID::Palette44); + isGhost = true; } else if (OpenRCT2::TileInspector::IsElementSelected(reinterpret_cast(&tileElement))) { - imageId = imageId.WithRemap(FilterPaletteID::Palette44); - imageFlags = CONSTRUCTION_MARKER; - } - - int32_t boxlengthZ = tile->z_clearance; - if (boxlengthZ > 0x80) - { - boxlengthZ = 0x80; - } - boxlengthZ -= 3; - uint16_t edi = tile->flags; - int32_t esi = 16; - if (edi & 0xF00) - { - edi &= 0xF000; - edi = Numerics::rol16(edi, direction); - esi = (edi & 0xF) | (edi >> 12); - } - const CoordsXYZ bbOffset = { s98E3C4[esi].offset, height }; - const CoordsXYZ bbLength = { s98E3C4[esi].length, boxlengthZ }; - PaintAddImageAsParent(session, imageId, { 0, 0, height }, bbLength, bbOffset); - if (sceneryEntry->scrolling_mode == SCROLLING_MODE_NONE || direction == 1 || direction == 2) - { - large_scenery_paint_supports(session, direction, height, tileElement, imageFlags, tile); - return; - } - if (sceneryEntry->flags & LARGE_SCENERY_FLAG_3D_TEXT) - { - if (sceneryEntry->tiles[1].x_offset != static_cast(static_cast(0xFFFF))) - { - int32_t sequenceDirection = (tileElement.GetSequenceIndex() - 1) & 3; - if (sequenceDirection != direction) - { - large_scenery_paint_supports(session, direction, height, tileElement, imageFlags, tile); - return; - } - } - rct_drawpixelinfo* dpi = &session->DPI; - if (dpi->zoom_level > ZoomLevel{ 1 }) - { - large_scenery_paint_supports(session, direction, height, tileElement, imageFlags, tile); - return; - } - // 6B8331: - // Draw sign text: - int32_t textColour = tileElement.GetSecondaryColour(); - if (imageFlags) - { - textColour = COLOUR_GREY; - } - textColour = (textColour << 19) | IMAGE_TYPE_REMAP; - auto banner = tileElement.GetBanner(); - if (banner != nullptr) - { - auto ft = Formatter(); - banner->FormatTextTo(ft); - utf8 signString[256]; - format_string(signString, sizeof(signString), STR_STRINGID, ft.Data()); - LargeSceneryText* text = sceneryEntry->text; - int32_t y_offset = (text->offset[(direction & 1)].y * 2); - if (text->flags & LARGE_SCENERY_TEXT_FLAG_VERTICAL) - { - // Draw vertical sign: - y_offset += 1; - utf8 fitStr[32]; - large_scenery_sign_fit_text(signString, text, true, fitStr, sizeof(fitStr)); - safe_strcpy(fitStr, fitStr, sizeof(fitStr)); - const utf8* fitStrPtr = fitStr; - int32_t height2 = large_scenery_sign_text_height(fitStr, text); - uint32_t codepoint; - while ((codepoint = utf8_get_next(fitStrPtr, &fitStrPtr)) != 0) - { - utf8 str[5] = { 0 }; - utf8_write_codepoint(str, codepoint); - large_scenery_sign_paint_line( - session, str, sceneryEntry->text, sceneryEntry->text_image, textColour, direction, y_offset - height2); - y_offset += large_scenery_sign_get_glyph(text, codepoint)->height * 2; - } - } - else - { - y_offset -= (direction & 1); - if (text->flags & LARGE_SCENERY_TEXT_FLAG_TWO_LINE) - { - // Draw two-line sign: - int32_t width = large_scenery_sign_text_width(signString, text); - if (width > text->max_width) - { - y_offset -= large_scenery_sign_get_glyph(text, 'A')->height + 1; - utf8* src = signString; - for (int32_t i = 0; i < 2; i++) - { - utf8 str1[64] = { 0 }; - utf8* dst = str1; - utf8* srcold = src; - utf8* spacesrc = nullptr; - utf8* spacedst = nullptr; - int32_t w = 0; - uint32_t codepoint = utf8_get_next(src, const_cast(&src)); - do - { - w += large_scenery_sign_get_glyph(text, codepoint)->width; - if (codepoint == ' ') - { - spacesrc = src; - spacedst = dst; - } - } while (w <= text->max_width && (dst = utf8_write_codepoint(dst, codepoint)) != nullptr - && (srcold = src) != nullptr - && (codepoint = utf8_get_next(src, const_cast(&src))) != '\0'); - src = srcold; - if (spacesrc && codepoint) - { - *spacedst = 0; - src = spacesrc; - } - large_scenery_sign_paint_line( - session, str1, sceneryEntry->text, sceneryEntry->text_image, textColour, direction, y_offset); - y_offset += (large_scenery_sign_get_glyph(text, 'A')->height + 1) * 2; - } - } - else - { - large_scenery_sign_paint_line( - session, signString, sceneryEntry->text, sceneryEntry->text_image, textColour, direction, y_offset); - } - } - else - { - // Draw one-line sign: - large_scenery_sign_paint_line( - session, signString, sceneryEntry->text, sceneryEntry->text_image, textColour, direction, y_offset); - } - } - } - return; - } - rct_drawpixelinfo* dpi = &session->DPI; - if (dpi->zoom_level > ZoomLevel{ 0 }) - { - large_scenery_paint_supports(session, direction, height, tileElement, imageFlags, tile); - return; - } - uint8_t sequenceDirection2 = (tileElement.GetSequenceIndex() - 1) & 3; - if (sequenceDirection2 != direction) - { - large_scenery_paint_supports(session, direction, height, tileElement, imageFlags, tile); - return; - } - // Draw scrolling text: - uint8_t textColour = tileElement.GetSecondaryColour(); - if (imageFlags) - { - textColour = COLOUR_GREY; - } - if (direction == 0) - { - textColour = ColourMapA[textColour].mid_dark; + imageTemplate = ImageId().WithRemap(FilterPaletteID::Palette44); + isGhost = true; } else { - textColour = ColourMapA[textColour].light; + imageTemplate = ImageId(0, tileElement.GetPrimaryColour(), tileElement.GetSecondaryColour()); } - // 6B809A: - uint16_t scrollMode = sceneryEntry->scrolling_mode + ((direction + 1) & 0x3); - auto banner = tileElement.GetBanner(); - if (banner != nullptr) + + auto boxlengthZ = std::min(tile->z_clearance, 128) - 3; + auto flags = tile->flags; + auto bbIndex = 16; + if (flags & 0xF00) { - auto ft = Formatter(); - banner->FormatTextTo(ft); - utf8 signString[256]; - if (gConfigGeneral.upper_case_banners) - { - format_string_to_upper(signString, sizeof(signString), STR_SCROLLING_SIGN_TEXT, ft.Data()); - } - else - { - format_string(signString, sizeof(signString), STR_SCROLLING_SIGN_TEXT, ft.Data()); - } - - uint16_t stringWidth = gfx_get_string_width(signString, FontSpriteBase::TINY); - uint16_t scroll = stringWidth > 0 ? (gCurrentTicks / 2) % stringWidth : 0; - PaintAddImageAsChild( - session, scrolling_text_setup(session, STR_SCROLLING_SIGN_TEXT, ft, scroll, scrollMode, textColour), 0, 0, 1, 1, 21, - height + 25, bbOffset.x, bbOffset.y, bbOffset.z); + flags &= 0xF000; + flags = Numerics::rol16(flags, direction); + bbIndex = (flags & 0xF) | (flags >> 12); } + const CoordsXYZ bbOffset = { LargeSceneryBoundBoxes[bbIndex].offset, height }; + const CoordsXYZ bbLength = { LargeSceneryBoundBoxes[bbIndex].length, boxlengthZ }; - large_scenery_paint_supports(session, direction, height, tileElement, imageFlags, tile); + auto imageIndex = sceneryEntry->image + 4 + (sequenceNum << 2) + direction; + PaintAddImageAsParent(session, imageTemplate.WithIndex(imageIndex), { 0, 0, height }, bbLength, bbOffset); + + if (sceneryEntry->scrolling_mode != SCROLLING_MODE_NONE && direction != 1 && direction != 2) + { + if (sceneryEntry->flags & LARGE_SCENERY_FLAG_3D_TEXT) + { + PaintLargeScenery3DText(session, *sceneryEntry, *tile, tileElement, direction, height, isGhost); + } + else if (session->DPI.zoom_level <= ZoomLevel{ 0 }) + { + auto sequenceDirection2 = (tileElement.GetSequenceIndex() - 1) & 3; + if (sequenceDirection2 == direction) + { + PaintLargeSceneryScrollingText(session, *sceneryEntry, tileElement, direction, height, bbOffset, isGhost); + } + } + } + PaintLargeScenerySupports(session, direction, height, tileElement, isGhost ? imageTemplate : ImageId(), *tile); } diff --git a/src/openrct2/world/Scenery.cpp b/src/openrct2/world/Scenery.cpp index 051bf21ba2..ed09e50785 100644 --- a/src/openrct2/world/Scenery.cpp +++ b/src/openrct2/world/Scenery.cpp @@ -19,6 +19,7 @@ #include "../actions/SmallSceneryRemoveAction.h" #include "../actions/WallRemoveAction.h" #include "../common.h" +#include "../core/String.hpp" #include "../entity/Fountain.h" #include "../localisation/Localisation.h" #include "../network/network.h" @@ -64,6 +65,61 @@ const CoordsXY SceneryQuadrantOffsets[] = { { 23, 7 }, }; +LargeSceneryText::LargeSceneryText(const rct_large_scenery_text& original) +{ + for (size_t i = 0; i < std::size(original.offset); i++) + { + offset[i].x = original.offset[i].x; + offset[i].y = original.offset[i].y; + } + max_width = original.max_width; + flags = original.flags; + num_images = original.num_images; + for (size_t i = 0; i < std::size(original.glyphs); i++) + { + glyphs[i] = original.glyphs[i]; + } +} + +const rct_large_scenery_text_glyph* LargeSceneryText::GetGlyph(char32_t codepoint) const +{ + if (codepoint >= std::size(glyphs)) + { + return nullptr; + } + return &glyphs[codepoint]; +} + +const rct_large_scenery_text_glyph& LargeSceneryText::GetGlyph(char32_t codepoint, char32_t defaultCodepoint) const +{ + auto glyph = GetGlyph(codepoint); + if (glyph == nullptr) + { + glyph = GetGlyph(defaultCodepoint); + } + return *glyph; +} + +int32_t LargeSceneryText::MeasureWidth(std::string_view text) const +{ + auto result = 0; + for (auto codepoint : CodepointView(text)) + { + result += GetGlyph(codepoint, ' ').width; + } + return result; +} + +int32_t LargeSceneryText::MeasureHeight(std::string_view text) const +{ + auto result = 0; + for (auto codepoint : CodepointView(text)) + { + result += GetGlyph(codepoint, ' ').height; + } + return result; +} + void scenery_update_tile(const CoordsXY& sceneryPos) { TileElement* tileElement; diff --git a/src/openrct2/world/Scenery.h b/src/openrct2/world/Scenery.h index 6cc3356cab..67fdda736b 100644 --- a/src/openrct2/world/Scenery.h +++ b/src/openrct2/world/Scenery.h @@ -15,6 +15,7 @@ #include "TileElement.h" #include +#include #define SCENERY_WITHER_AGE_THRESHOLD_1 0x28 #define SCENERY_WITHER_AGE_THRESHOLD_2 0x37 @@ -178,22 +179,11 @@ struct LargeSceneryText rct_large_scenery_text_glyph glyphs[256]; LargeSceneryText() = default; - - explicit LargeSceneryText(const rct_large_scenery_text& original) - { - for (size_t i = 0; i < std::size(original.offset); i++) - { - offset[i].x = original.offset[i].x; - offset[i].y = original.offset[i].y; - } - max_width = original.max_width; - flags = original.flags; - num_images = original.num_images; - for (size_t i = 0; i < std::size(original.glyphs); i++) - { - glyphs[i] = original.glyphs[i]; - } - } + explicit LargeSceneryText(const rct_large_scenery_text& original); + const rct_large_scenery_text_glyph* GetGlyph(char32_t codepoint) const; + const rct_large_scenery_text_glyph& GetGlyph(char32_t codepoint, char32_t defaultCodepoint) const; + int32_t MeasureWidth(std::string_view text) const; + int32_t MeasureHeight(std::string_view text) const; }; struct rct_scenery_group_entry