mirror of
https://github.com/OpenRCT2/OpenRCT2
synced 2026-01-27 08:45:00 +01:00
464 lines
16 KiB
C++
464 lines
16 KiB
C++
/*****************************************************************************
|
|
* Copyright (c) 2014-2019 OpenRCT2 developers
|
|
*
|
|
* For a complete list of all authors, please refer to contributors.md
|
|
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
|
*
|
|
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
|
*****************************************************************************/
|
|
|
|
#include "../../Game.h"
|
|
#include "../../config/Config.h"
|
|
#include "../../interface/Viewport.h"
|
|
#include "../../localisation/Localisation.h"
|
|
#include "../../ride/Ride.h"
|
|
#include "../../ride/TrackDesign.h"
|
|
#include "../../util/Util.h"
|
|
#include "../../world/Banner.h"
|
|
#include "../../world/LargeScenery.h"
|
|
#include "../../world/Map.h"
|
|
#include "../../world/Scenery.h"
|
|
#include "../Paint.h"
|
|
#include "../Supports.h"
|
|
#include "Paint.TileElement.h"
|
|
|
|
#include <iterator>
|
|
|
|
// 6B8172:
|
|
static void large_scenery_paint_supports(
|
|
paint_session* session, uint8_t direction, uint16_t height, const TileElement* tileElement, uint32_t dword_F4387C,
|
|
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, nullptr);
|
|
|
|
int32_t clearanceHeight = ceil2(tileElement->clearance_height * 8 + 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(rct_large_scenery_text* text, uint32_t codepoint)
|
|
{
|
|
if (codepoint >= std::size(text->glyphs))
|
|
{
|
|
return &text->glyphs[(size_t)'?'];
|
|
}
|
|
return &text->glyphs[codepoint];
|
|
}
|
|
|
|
static int32_t large_scenery_sign_text_width(const utf8* str, rct_large_scenery_text* 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, rct_large_scenery_text* 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, rct_large_scenery_text* 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 utf8**)&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, rct_large_scenery_text* 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)
|
|
{
|
|
paint_attach_to_previous_ps(session, image_id, x_offset, -div_to_minus_infinity(acc, 2));
|
|
}
|
|
else
|
|
{
|
|
if (text->flags & LARGE_SCENERY_TEXT_FLAG_VERTICAL)
|
|
{
|
|
paint_attach_to_previous_ps(session, image_id, x_offset, div_to_minus_infinity(acc, 2));
|
|
}
|
|
else
|
|
{
|
|
paint_attach_to_previous_attach(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
|
|
{
|
|
LocationXY16 offset;
|
|
LocationXY16 length;
|
|
};
|
|
|
|
// clang-format off
|
|
static constexpr const boundbox s98E3C4[] = {
|
|
{ 3, 3, 26, 26 },
|
|
{ 17, 17, 12, 12 },
|
|
{ 17, 3, 12, 12 },
|
|
{ 17, 3, 12, 26 },
|
|
{ 3, 3, 12, 12 },
|
|
{ 3, 3, 26, 26 },
|
|
{ 3, 3, 28, 12 },
|
|
{ 3, 3, 26, 26 },
|
|
{ 3, 17, 12, 12 },
|
|
{ 3, 17, 26, 12 },
|
|
{ 3, 3, 26, 26 },
|
|
{ 3, 3, 26, 26 },
|
|
{ 3, 3, 12, 28 },
|
|
{ 3, 3, 26, 26 },
|
|
{ 3, 3, 26, 26 },
|
|
{ 3, 3, 26, 26 },
|
|
{ 1, 1, 30, 30 },
|
|
};
|
|
// clang-format on
|
|
|
|
/*
|
|
*
|
|
* rct2: 0x006B7F0C
|
|
*/
|
|
void large_scenery_paint(paint_session* session, uint8_t direction, uint16_t height, const TileElement* tileElement)
|
|
{
|
|
if (session->ViewFlags & VIEWPORT_FLAG_HIGHLIGHT_PATH_ISSUES)
|
|
{
|
|
return;
|
|
}
|
|
session->InteractionType = VIEWPORT_INTERACTION_ITEM_LARGE_SCENERY;
|
|
uint32_t sequenceNum = tileElement->AsLargeScenery()->GetSequenceIndex();
|
|
rct_scenery_entry* entry = tileElement->AsLargeScenery()->GetEntry();
|
|
if (entry == nullptr)
|
|
return;
|
|
|
|
uint32_t image_id = (sequenceNum << 2) + entry->image + 4 + direction;
|
|
rct_large_scenery_tile* tile = &entry->large_scenery.tiles[sequenceNum];
|
|
uint32_t dword_F4387C = 0;
|
|
image_id |= SPRITE_ID_PALETTE_COLOUR_2(
|
|
tileElement->AsLargeScenery()->GetPrimaryColour(), tileElement->AsLargeScenery()->GetSecondaryColour());
|
|
LocationXYZ16 boxlength;
|
|
LocationXYZ16 boxoffset;
|
|
if (gTrackDesignSaveMode)
|
|
{
|
|
if (!track_design_save_contains_tile_element(tileElement))
|
|
{
|
|
sequenceNum = SPRITE_ID_PALETTE_COLOUR_1(PALETTE_46);
|
|
image_id &= 0x7FFFF;
|
|
dword_F4387C = sequenceNum;
|
|
image_id |= dword_F4387C;
|
|
}
|
|
}
|
|
if (tileElement->IsGhost())
|
|
{
|
|
session->InteractionType = VIEWPORT_INTERACTION_ITEM_NONE;
|
|
sequenceNum = CONSTRUCTION_MARKER;
|
|
image_id &= 0x7FFFF;
|
|
dword_F4387C = sequenceNum;
|
|
image_id |= dword_F4387C;
|
|
}
|
|
int32_t ah = tile->z_clearance;
|
|
if (ah > 0x80)
|
|
{
|
|
ah = 0x80;
|
|
}
|
|
ah -= 3;
|
|
uint16_t edi = tile->flags;
|
|
int32_t esi = 16;
|
|
if (edi & 0xF00)
|
|
{
|
|
edi &= 0xF000;
|
|
edi = rol16(edi, direction);
|
|
esi = (edi & 0xF) | (edi >> 12);
|
|
}
|
|
boxoffset.x = s98E3C4[esi].offset.x;
|
|
boxoffset.y = s98E3C4[esi].offset.y;
|
|
boxoffset.z = height;
|
|
boxlength.x = s98E3C4[esi].length.x;
|
|
boxlength.y = s98E3C4[esi].length.y;
|
|
boxlength.z = ah;
|
|
sub_98197C(session, image_id, 0, 0, boxlength.x, boxlength.y, ah, height, boxoffset.x, boxoffset.y, boxoffset.z);
|
|
if (entry->large_scenery.scrolling_mode == SCROLLING_MODE_NONE || direction == 1 || direction == 2)
|
|
{
|
|
large_scenery_paint_supports(session, direction, height, tileElement, dword_F4387C, tile);
|
|
return;
|
|
}
|
|
if (entry->large_scenery.flags & LARGE_SCENERY_FLAG_3D_TEXT)
|
|
{
|
|
if (entry->large_scenery.tiles[1].x_offset != (int16_t)(uint16_t)0xFFFF)
|
|
{
|
|
int32_t sequenceDirection = (tileElement->AsLargeScenery()->GetSequenceIndex() - 1) & 3;
|
|
if (sequenceDirection != direction)
|
|
{
|
|
large_scenery_paint_supports(session, direction, height, tileElement, dword_F4387C, tile);
|
|
return;
|
|
}
|
|
}
|
|
rct_drawpixelinfo* dpi = &session->DPI;
|
|
if (dpi->zoom_level > 1)
|
|
{
|
|
large_scenery_paint_supports(session, direction, height, tileElement, dword_F4387C, tile);
|
|
return;
|
|
}
|
|
// 6B8331:
|
|
// Draw sign text:
|
|
set_format_arg(0, uint32_t, 0);
|
|
set_format_arg(4, uint32_t, 0);
|
|
int32_t textColour = tileElement->AsLargeScenery()->GetSecondaryColour();
|
|
if (dword_F4387C)
|
|
{
|
|
textColour = COLOUR_GREY;
|
|
}
|
|
textColour = (textColour << 19) | IMAGE_TYPE_REMAP;
|
|
auto banner = tileElement->AsLargeScenery()->GetBanner();
|
|
if (banner != nullptr)
|
|
{
|
|
auto stringId = banner->string_idx;
|
|
if (banner->flags & BANNER_FLAG_LINKED_TO_RIDE)
|
|
{
|
|
auto ride = get_ride(banner->ride_index);
|
|
stringId = ride->name;
|
|
set_format_arg(0, uint32_t, ride->name_arguments);
|
|
}
|
|
utf8 signString[256];
|
|
format_string(signString, sizeof(signString), stringId, gCommonFormatArgs);
|
|
rct_large_scenery_text* text = entry->large_scenery.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, entry->large_scenery.text, entry->large_scenery.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 utf8**)&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 utf8**)&src)) != '\0');
|
|
src = srcold;
|
|
if (spacesrc && codepoint)
|
|
{
|
|
*spacedst = 0;
|
|
src = spacesrc;
|
|
}
|
|
large_scenery_sign_paint_line(
|
|
session, str1, entry->large_scenery.text, entry->large_scenery.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, entry->large_scenery.text, entry->large_scenery.text_image, textColour,
|
|
direction, y_offset);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Draw one-line sign:
|
|
large_scenery_sign_paint_line(
|
|
session, signString, entry->large_scenery.text, entry->large_scenery.text_image, textColour, direction,
|
|
y_offset);
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
rct_drawpixelinfo* dpi = &session->DPI;
|
|
if (dpi->zoom_level > 0)
|
|
{
|
|
large_scenery_paint_supports(session, direction, height, tileElement, dword_F4387C, tile);
|
|
return;
|
|
}
|
|
uint8_t sequenceDirection2 = (tileElement->AsLargeScenery()->GetSequenceIndex() - 1) & 3;
|
|
if (sequenceDirection2 != direction)
|
|
{
|
|
large_scenery_paint_supports(session, direction, height, tileElement, dword_F4387C, tile);
|
|
return;
|
|
}
|
|
// Draw scrolling text:
|
|
set_format_arg(0, uint32_t, 0);
|
|
set_format_arg(4, uint32_t, 0);
|
|
uint8_t textColour = tileElement->AsLargeScenery()->GetSecondaryColour();
|
|
if (dword_F4387C)
|
|
{
|
|
textColour = COLOUR_GREY;
|
|
}
|
|
if (direction == 3)
|
|
{
|
|
textColour |= (1 << 7);
|
|
}
|
|
// 6B809A:
|
|
set_format_arg(7, uint8_t, textColour);
|
|
BannerIndex bannerIndex = tileElement->AsLargeScenery()->GetBannerIndex();
|
|
uint16_t scrollMode = entry->large_scenery.scrolling_mode + ((direction + 1) & 0x3);
|
|
auto banner = GetBanner(bannerIndex);
|
|
set_format_arg(0, rct_string_id, banner->string_idx);
|
|
if (banner->flags & BANNER_FLAG_LINKED_TO_RIDE)
|
|
{
|
|
Ride* ride = get_ride(banner->ride_index);
|
|
set_format_arg(0, rct_string_id, ride->name);
|
|
set_format_arg(2, uint32_t, ride->name_arguments);
|
|
}
|
|
utf8 signString[256];
|
|
rct_string_id stringId = STR_SCROLLING_SIGN_TEXT;
|
|
if (gConfigGeneral.upper_case_banners)
|
|
{
|
|
format_string_to_upper(signString, sizeof(signString), stringId, gCommonFormatArgs);
|
|
}
|
|
else
|
|
{
|
|
format_string(signString, sizeof(signString), stringId, gCommonFormatArgs);
|
|
}
|
|
|
|
gCurrentFontSpriteBase = FONT_SPRITE_BASE_TINY;
|
|
|
|
uint16_t string_width = gfx_get_string_width(signString);
|
|
uint16_t scroll = (gCurrentTicks / 2) % string_width;
|
|
sub_98199C(
|
|
session, scrolling_text_setup(session, stringId, scroll, scrollMode), 0, 0, 1, 1, 21, height + 25, boxoffset.x,
|
|
boxoffset.y, boxoffset.z);
|
|
|
|
large_scenery_paint_supports(session, direction, height, tileElement, dword_F4387C, tile);
|
|
}
|