mirror of
https://github.com/OpenTTD/OpenTTD.git
synced 2025-03-06 14:27:16 +00:00
Codechange: Replace viewport StringID specialisation with flags. (#13237)
When drawing viewport strings, the StringID is used to determine how to draw the sign. Instead, allow the behaviour to be set by the caller with flags. This means that some of the viewport-specific strings are no longer necessary. ViewportAddString() now returns a pointer to a string as it may not actually add the string, in which case preparing the string parameters in advance is a waste of time.
This commit is contained in:
parent
fd7a883cbd
commit
810dc23215
@ -5830,19 +5830,8 @@ STR_SAVEGAME_NAME_DEFAULT :{COMPANY}, {STR
|
||||
STR_SAVEGAME_NAME_SPECTATOR :Spectator, {1:STRING2}
|
||||
|
||||
# Viewport strings
|
||||
STR_VIEWPORT_TOWN_POP :{WHITE}{TOWN} ({COMMA})
|
||||
STR_VIEWPORT_TOWN :{WHITE}{TOWN}
|
||||
STR_VIEWPORT_TOWN_TINY_BLACK :{TINY_FONT}{BLACK}{TOWN}
|
||||
STR_VIEWPORT_TOWN_TINY_WHITE :{TINY_FONT}{WHITE}{TOWN}
|
||||
|
||||
STR_VIEWPORT_SIGN_SMALL_BLACK :{TINY_FONT}{BLACK}{SIGN}
|
||||
STR_VIEWPORT_SIGN_SMALL_WHITE :{TINY_FONT}{WHITE}{SIGN}
|
||||
|
||||
STR_VIEWPORT_TOWN_POP :{TOWN} ({COMMA})
|
||||
STR_VIEWPORT_STATION :{STATION} {STATION_FEATURES}
|
||||
STR_VIEWPORT_STATION_TINY :{TINY_FONT}{STATION}
|
||||
|
||||
STR_VIEWPORT_WAYPOINT :{WAYPOINT}
|
||||
STR_VIEWPORT_WAYPOINT_TINY :{TINY_FONT}{WAYPOINT}
|
||||
|
||||
# Simple strings to get specific types of data
|
||||
STR_COMPANY_NAME :{COMPANY}
|
||||
|
@ -450,7 +450,7 @@ void Station::UpdateVirtCoord()
|
||||
|
||||
SetDParam(0, this->index);
|
||||
SetDParam(1, this->facilities);
|
||||
this->sign.UpdatePosition(pt.x, pt.y, STR_VIEWPORT_STATION, STR_VIEWPORT_STATION_TINY);
|
||||
this->sign.UpdatePosition(pt.x, pt.y, STR_VIEWPORT_STATION, STR_STATION_NAME);
|
||||
|
||||
_viewport_sign_kdtree.Insert(ViewportSignKdtreeItem::MakeStation(this->index));
|
||||
|
||||
|
@ -120,11 +120,19 @@ void DrawTextEffects(DrawPixelInfo *dpi)
|
||||
/* Don't draw the text effects when zoomed out a lot */
|
||||
if (dpi->zoom > ZOOM_LVL_TEXT_EFFECT) return;
|
||||
if (IsTransparencySet(TO_TEXT)) return;
|
||||
for (TextEffect &te : _text_effects) {
|
||||
|
||||
ViewportStringFlags flags{};
|
||||
if (dpi->zoom >= ZOOM_LVL_TEXT_EFFECT) flags |= ViewportStringFlags::Small;
|
||||
|
||||
for (const TextEffect &te : _text_effects) {
|
||||
if (te.string_id == INVALID_STRING_ID) continue;
|
||||
|
||||
if (te.mode == TE_RISING || _settings_client.gui.loading_indicators) {
|
||||
std::string *str = ViewportAddString(dpi, &te, flags, INVALID_COLOUR);
|
||||
if (str == nullptr) continue;
|
||||
|
||||
CopyInDParam(te.params);
|
||||
ViewportAddString(dpi, ZOOM_LVL_TEXT_EFFECT, &te, te.string_id, te.string_id, STR_NULL);
|
||||
*str = GetString(te.string_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -415,8 +415,8 @@ void Town::UpdateVirtCoord()
|
||||
SetDParam(0, this->index);
|
||||
SetDParam(1, this->cache.population);
|
||||
this->cache.sign.UpdatePosition(pt.x, pt.y - 24 * ZOOM_BASE,
|
||||
_settings_client.gui.population_in_label ? STR_VIEWPORT_TOWN_POP : STR_VIEWPORT_TOWN,
|
||||
STR_VIEWPORT_TOWN_TINY_WHITE);
|
||||
_settings_client.gui.population_in_label ? STR_VIEWPORT_TOWN_POP : STR_TOWN_NAME,
|
||||
STR_TOWN_NAME);
|
||||
|
||||
_viewport_sign_kdtree.Insert(ViewportSignKdtreeItem::MakeTown(this->index));
|
||||
|
||||
|
200
src/viewport.cpp
200
src/viewport.cpp
@ -113,11 +113,11 @@ static const int MAX_TILE_EXTENT_BOTTOM = ZOOM_BASE * (TILE_PIXELS + 2 * TILE_HE
|
||||
|
||||
struct StringSpriteToDraw {
|
||||
std::string string;
|
||||
StringID string_id;
|
||||
uint16_t width;
|
||||
Colours colour;
|
||||
ViewportStringFlags flags;
|
||||
int32_t x;
|
||||
int32_t y;
|
||||
uint16_t width;
|
||||
};
|
||||
|
||||
struct TileSpriteToDraw {
|
||||
@ -861,16 +861,26 @@ void AddChildSpriteScreen(SpriteID image, PaletteID pal, int x, int y, bool tran
|
||||
_vd.last_child = child_id;
|
||||
}
|
||||
|
||||
static void AddStringToDraw(int x, int y, StringID string, Colours colour, uint16_t width)
|
||||
/**
|
||||
* Add a string to draw to a viewport.
|
||||
* @param x Left position of string.
|
||||
* @param y Top position of string.
|
||||
* @param colour Colour of string.
|
||||
* @param flags ViewportStringFlags to control the string's appearance.
|
||||
* @param width Width of the string.
|
||||
* @returns Reference to raw string which should be filled in by the caller.
|
||||
*/
|
||||
static std::string &AddStringToDraw(int x, int y, Colours colour, ViewportStringFlags flags, uint16_t width)
|
||||
{
|
||||
assert(width != 0);
|
||||
StringSpriteToDraw &ss = _vd.string_sprites_to_draw.emplace_back();
|
||||
ss.string = GetString(string);
|
||||
ss.string_id = string;
|
||||
ss.colour = colour;
|
||||
ss.flags = flags;
|
||||
ss.x = x;
|
||||
ss.y = y;
|
||||
ss.width = width;
|
||||
ss.colour = colour;
|
||||
|
||||
return ss.string;
|
||||
}
|
||||
|
||||
|
||||
@ -1299,24 +1309,22 @@ static void ViewportAddLandscape()
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a string to draw in the viewport
|
||||
* Add a string to draw in the current viewport.
|
||||
* @param dpi current viewport area
|
||||
* @param small_from Zoomlevel from when the small font should be used
|
||||
* @param sign sign position and dimension
|
||||
* @param string_normal String for normal and 2x zoom level
|
||||
* @param string_small String for 4x and 8x zoom level
|
||||
* @param string_small_shadow Shadow string for 4x and 8x zoom level; or #STR_NULL if no shadow
|
||||
* @param flags ViewportStringFlags to control the string's appearance.
|
||||
* @param colour colour of the sign background; or INVALID_COLOUR if transparent
|
||||
* @returns Pointer to std::string to filled with sign, or nullptr if string would be outside the viewport bounds.
|
||||
*/
|
||||
void ViewportAddString(const DrawPixelInfo *dpi, ZoomLevel small_from, const ViewportSign *sign, StringID string_normal, StringID string_small, StringID string_small_shadow, Colours colour)
|
||||
std::string *ViewportAddString(const DrawPixelInfo *dpi, const ViewportSign *sign, ViewportStringFlags flags, Colours colour)
|
||||
{
|
||||
bool small = dpi->zoom >= small_from;
|
||||
|
||||
int left = dpi->left;
|
||||
int top = dpi->top;
|
||||
int right = left + dpi->width;
|
||||
int bottom = top + dpi->height;
|
||||
|
||||
bool small = HasFlag(flags, ViewportStringFlags::Small);
|
||||
int sign_height = ScaleByZoom(WidgetDimensions::scaled.fullbevel.top + GetCharacterHeight(small ? FS_SMALL : FS_NORMAL) + WidgetDimensions::scaled.fullbevel.bottom, dpi->zoom);
|
||||
int sign_half_width = ScaleByZoom((small ? sign->width_small : sign->width_normal) / 2, dpi->zoom);
|
||||
|
||||
@ -1324,19 +1332,10 @@ void ViewportAddString(const DrawPixelInfo *dpi, ZoomLevel small_from, const Vie
|
||||
top > sign->top + sign_height ||
|
||||
right < sign->center - sign_half_width ||
|
||||
left > sign->center + sign_half_width) {
|
||||
return;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!small) {
|
||||
AddStringToDraw(sign->center - sign_half_width, sign->top, string_normal, colour, sign->width_normal);
|
||||
} else {
|
||||
int shadow_offset = 0;
|
||||
if (string_small_shadow != STR_NULL) {
|
||||
shadow_offset = 4;
|
||||
AddStringToDraw(sign->center - sign_half_width + shadow_offset, sign->top, string_small_shadow, INVALID_COLOUR, sign->width_small | 0x8000);
|
||||
}
|
||||
AddStringToDraw(sign->center - sign_half_width, sign->top - shadow_offset, string_small, colour, sign->width_small | 0x8000);
|
||||
}
|
||||
return &AddStringToDraw(sign->center - sign_half_width, sign->top, colour, flags, small ? sign->width_small : sign->width_normal);
|
||||
}
|
||||
|
||||
static Rect ExpandRectWithViewportSignMargins(Rect r, ZoomLevel zoom)
|
||||
@ -1354,6 +1353,80 @@ static Rect ExpandRectWithViewportSignMargins(Rect r, ZoomLevel zoom)
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add town strings to a viewport.
|
||||
* @param dpi Current viewport area.
|
||||
* @param towns List of towns to add.
|
||||
* @param small Add small versions of strings.
|
||||
*/
|
||||
static void ViewportAddTownStrings(DrawPixelInfo *dpi, const std::vector<const Town *> &towns, bool small)
|
||||
{
|
||||
ViewportStringFlags flags{};
|
||||
if (small) flags = ViewportStringFlags::Small | ViewportStringFlags::Shadow;
|
||||
|
||||
StringID stringid = !small && _settings_client.gui.population_in_label ? STR_VIEWPORT_TOWN_POP : STR_TOWN_NAME;
|
||||
for (const Town *t : towns) {
|
||||
std::string *str = ViewportAddString(dpi, &t->cache.sign, flags, INVALID_COLOUR);
|
||||
if (str == nullptr) continue;
|
||||
|
||||
SetDParam(0, t->index);
|
||||
SetDParam(1, t->cache.population);
|
||||
*str = GetString(stringid);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add sign strings to a viewport.
|
||||
* @param dpi Current viewport area.
|
||||
* @param towns List of signs to add.
|
||||
* @param small Add small versions of strings.
|
||||
*/
|
||||
static void ViewportAddSignStrings(DrawPixelInfo *dpi, const std::vector<const Sign *> &signs, bool small)
|
||||
{
|
||||
ViewportStringFlags flags{};
|
||||
if (small) flags = ViewportStringFlags::Small;
|
||||
|
||||
/* Signs placed by a game script don't have a frame. */
|
||||
ViewportStringFlags deity_flags{flags};
|
||||
flags |= IsTransparencySet(TO_SIGNS) ? ViewportStringFlags::TransparentRect : ViewportStringFlags::ColourRect;
|
||||
|
||||
for (const Sign *si : signs) {
|
||||
std::string *str = ViewportAddString(dpi, &si->sign, (si->owner == OWNER_DEITY) ? deity_flags : flags,
|
||||
(si->owner == OWNER_NONE) ? COLOUR_GREY : (si->owner == OWNER_DEITY ? INVALID_COLOUR : _company_colours[si->owner]));
|
||||
if (str == nullptr) continue;
|
||||
|
||||
SetDParam(0, si->index);
|
||||
*str = GetString(STR_SIGN_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add station strings to a viewport.
|
||||
* @param dpi Current viewport area.
|
||||
* @param towns List of stations to add.
|
||||
* @param small Add small versions of strings.
|
||||
*/
|
||||
static void ViewportAddStationStrings(DrawPixelInfo *dpi, const std::vector<const BaseStation *> &stations, bool small)
|
||||
{
|
||||
/* Transparent station signs have colour text instead of a colour panel. */
|
||||
ViewportStringFlags flags{IsTransparencySet(TO_SIGNS) ? ViewportStringFlags::TextColour : ViewportStringFlags::ColourRect};
|
||||
if (small) flags = ViewportStringFlags::Small;
|
||||
|
||||
for (const BaseStation *st : stations) {
|
||||
std::string *str = ViewportAddString(dpi, &st->sign, flags, (st->owner == OWNER_NONE || !st->IsInUse()) ? COLOUR_GREY : _company_colours[st->owner]);
|
||||
if (str == nullptr) continue;
|
||||
|
||||
if (Station::IsExpected(st)) { /* Station */
|
||||
SetDParam(0, st->index);
|
||||
SetDParam(1, st->facilities);
|
||||
*str = GetString(small ? STR_STATION_NAME : STR_VIEWPORT_STATION);
|
||||
} else { /* Waypoint */
|
||||
SetDParam(0, st->index);
|
||||
*str = GetString(STR_WAYPOINT_NAME);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ViewportAddKdtreeSigns(DrawPixelInfo *dpi)
|
||||
{
|
||||
Rect search_rect{ dpi->left, dpi->top, dpi->left + dpi->width, dpi->top + dpi->height };
|
||||
@ -1417,42 +1490,17 @@ static void ViewportAddKdtreeSigns(DrawPixelInfo *dpi)
|
||||
}
|
||||
});
|
||||
|
||||
/* Layering order (bottom to top): Town names, signs, stations */
|
||||
/* Small versions of signs are used zoom level 4X and higher. */
|
||||
bool small = dpi->zoom >= ZOOM_LVL_OUT_4X;
|
||||
|
||||
for (const auto *t : towns) {
|
||||
SetDParam(0, t->index);
|
||||
SetDParam(1, t->cache.population);
|
||||
ViewportAddString(dpi, ZOOM_LVL_OUT_4X, &t->cache.sign,
|
||||
_settings_client.gui.population_in_label ? STR_VIEWPORT_TOWN_POP : STR_VIEWPORT_TOWN,
|
||||
STR_VIEWPORT_TOWN_TINY_WHITE, STR_VIEWPORT_TOWN_TINY_BLACK);
|
||||
}
|
||||
/* Layering order (bottom to top): Town names, signs, stations */
|
||||
ViewportAddTownStrings(dpi, towns, small);
|
||||
|
||||
/* Do not draw signs nor station names if they are set invisible */
|
||||
if (IsInvisibilitySet(TO_SIGNS)) return;
|
||||
|
||||
for (const auto *si : signs) {
|
||||
SetDParam(0, si->index);
|
||||
ViewportAddString(dpi, ZOOM_LVL_OUT_4X, &si->sign,
|
||||
STR_WHITE_SIGN,
|
||||
(IsTransparencySet(TO_SIGNS) || si->owner == OWNER_DEITY) ? STR_VIEWPORT_SIGN_SMALL_WHITE : STR_VIEWPORT_SIGN_SMALL_BLACK, STR_NULL,
|
||||
(si->owner == OWNER_NONE) ? COLOUR_GREY : (si->owner == OWNER_DEITY ? INVALID_COLOUR : _company_colours[si->owner]));
|
||||
}
|
||||
|
||||
for (const auto *st : stations) {
|
||||
SetDParam(0, st->index);
|
||||
SetDParam(1, st->facilities);
|
||||
if (Station::IsExpected(st)) {
|
||||
/* Station */
|
||||
ViewportAddString(dpi, ZOOM_LVL_OUT_4X, &st->sign,
|
||||
STR_VIEWPORT_STATION, STR_VIEWPORT_STATION_TINY, STR_NULL,
|
||||
(st->owner == OWNER_NONE || !st->IsInUse()) ? COLOUR_GREY : _company_colours[st->owner]);
|
||||
} else {
|
||||
/* Waypoint */
|
||||
ViewportAddString(dpi, ZOOM_LVL_OUT_4X, &st->sign,
|
||||
STR_VIEWPORT_WAYPOINT, STR_VIEWPORT_WAYPOINT_TINY, STR_NULL,
|
||||
(st->owner == OWNER_NONE || !st->IsInUse()) ? COLOUR_GREY : _company_colours[st->owner]);
|
||||
}
|
||||
}
|
||||
ViewportAddSignStrings(dpi, signs, small);
|
||||
ViewportAddStationStrings(dpi, stations, small);
|
||||
}
|
||||
|
||||
|
||||
@ -1710,30 +1758,36 @@ static void ViewportDrawDirtyBlocks()
|
||||
static void ViewportDrawStrings(ZoomLevel zoom, const StringSpriteToDrawVector *sstdv)
|
||||
{
|
||||
for (const StringSpriteToDraw &ss : *sstdv) {
|
||||
TextColour colour = TC_BLACK;
|
||||
bool small = HasBit(ss.width, 15);
|
||||
int w = GB(ss.width, 0, 15);
|
||||
bool small = HasFlag(ss.flags, ViewportStringFlags::Small);
|
||||
int w = ss.width;
|
||||
int x = UnScaleByZoom(ss.x, zoom);
|
||||
int y = UnScaleByZoom(ss.y, zoom);
|
||||
int h = WidgetDimensions::scaled.fullbevel.top + GetCharacterHeight(small ? FS_SMALL : FS_NORMAL) + WidgetDimensions::scaled.fullbevel.bottom;
|
||||
|
||||
if (ss.colour != INVALID_COLOUR) {
|
||||
if (IsTransparencySet(TO_SIGNS) && ss.string_id != STR_WHITE_SIGN) {
|
||||
/* Don't draw the rectangle.
|
||||
* Real colours need the TC_IS_PALETTE_COLOUR flag.
|
||||
* Otherwise colours from _string_colourmap are assumed. */
|
||||
colour = (TextColour)GetColourGradient(ss.colour, SHADE_LIGHTER) | TC_IS_PALETTE_COLOUR;
|
||||
} else {
|
||||
/* Draw the rectangle if 'transparent station signs' is off,
|
||||
* or if we are drawing a general text sign (STR_WHITE_SIGN). */
|
||||
DrawFrameRect(
|
||||
x, y, x + w - 1, y + h - 1, ss.colour,
|
||||
IsTransparencySet(TO_SIGNS) ? FR_TRANSPARENT : FR_NONE
|
||||
);
|
||||
}
|
||||
TextColour colour = TC_WHITE;
|
||||
if (HasFlag(ss.flags, ViewportStringFlags::ColourRect)) {
|
||||
if (ss.colour != INVALID_COLOUR) DrawFrameRect(x, y, x + w - 1, y + h - 1, ss.colour, FR_NONE);
|
||||
colour = TC_BLACK;
|
||||
} else if (HasFlag(ss.flags, ViewportStringFlags::TransparentRect)) {
|
||||
DrawFrameRect(x, y, x + w - 1, y + h - 1, ss.colour, FR_TRANSPARENT);
|
||||
}
|
||||
|
||||
DrawString(x + WidgetDimensions::scaled.fullbevel.left, x + w - 1 - WidgetDimensions::scaled.fullbevel.right, y + WidgetDimensions::scaled.fullbevel.top, ss.string, colour, SA_HOR_CENTER, false, small ? FS_SMALL : FS_NORMAL);
|
||||
if (HasFlag(ss.flags, ViewportStringFlags::TextColour)) {
|
||||
if (ss.colour != INVALID_COLOUR) colour = static_cast<TextColour>(GetColourGradient(ss.colour, SHADE_LIGHTER) | TC_IS_PALETTE_COLOUR);
|
||||
}
|
||||
|
||||
int left = x + WidgetDimensions::scaled.fullbevel.left;
|
||||
int right = x + w - 1 - WidgetDimensions::scaled.fullbevel.right;
|
||||
int top = y + WidgetDimensions::scaled.fullbevel.top;
|
||||
|
||||
int shadow_offset = 0;
|
||||
if (small && HasFlag(ss.flags, ViewportStringFlags::Shadow)) {
|
||||
/* Shadow needs to be shifted 1 pixel. */
|
||||
shadow_offset = WidgetDimensions::scaled.fullbevel.top;
|
||||
DrawString(left + shadow_offset, right + shadow_offset, top, ss.string, TC_BLACK, SA_HOR_CENTER, false, FS_SMALL);
|
||||
}
|
||||
|
||||
DrawString(left, right, top - shadow_offset, ss.string, colour, SA_HOR_CENTER, false, small ? FS_SMALL : FS_NORMAL);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -54,7 +54,7 @@ void DrawGroundSprite(SpriteID image, PaletteID pal, const SubSprite *sub = null
|
||||
void DrawGroundSpriteAt(SpriteID image, PaletteID pal, int32_t x, int32_t y, int z, const SubSprite *sub = nullptr, int extra_offs_x = 0, int extra_offs_y = 0);
|
||||
void AddSortableSpriteToDraw(SpriteID image, PaletteID pal, int x, int y, int w, int h, int dz, int z, bool transparent = false, int bb_offset_x = 0, int bb_offset_y = 0, int bb_offset_z = 0, const SubSprite *sub = nullptr);
|
||||
void AddChildSpriteScreen(SpriteID image, PaletteID pal, int x, int y, bool transparent = false, const SubSprite *sub = nullptr, bool scale = true, bool relative = true);
|
||||
void ViewportAddString(const DrawPixelInfo *dpi, ZoomLevel small_from, const ViewportSign *sign, StringID string_normal, StringID string_small, StringID string_small_shadow, Colours colour = INVALID_COLOUR);
|
||||
std::string *ViewportAddString(const DrawPixelInfo *dpi, const ViewportSign *sign, ViewportStringFlags flags, Colours colour);
|
||||
|
||||
|
||||
void StartSpriteCombine();
|
||||
|
@ -10,12 +10,23 @@
|
||||
#ifndef VIEWPORT_TYPE_H
|
||||
#define VIEWPORT_TYPE_H
|
||||
|
||||
#include "core/enum_type.hpp"
|
||||
#include "zoom_type.h"
|
||||
#include "strings_type.h"
|
||||
#include "table/strings.h"
|
||||
|
||||
class LinkGraphOverlay;
|
||||
|
||||
/** Flags to control how Viewport Strings are rendered. */
|
||||
enum class ViewportStringFlags : uint8_t {
|
||||
Small = (1U << 0), ///< Draw using the small font.
|
||||
Shadow = (1U << 1), ///< Draw an extra text shadow. Should only be used with ViewportStringFlags::Small, as normal font already has a shadow.
|
||||
ColourRect = (1U << 2), ///< Draw a colour rect around the sign.
|
||||
TransparentRect = (1U << 3), ///< Draw a transparent rect around the sign.
|
||||
TextColour = (1U << 4), ///< Draw text in colour.
|
||||
};
|
||||
DECLARE_ENUM_AS_BIT_SET(ViewportStringFlags)
|
||||
|
||||
/**
|
||||
* Data structure for viewport, display of a part of the world
|
||||
*/
|
||||
|
@ -45,7 +45,7 @@ void Waypoint::UpdateVirtCoord()
|
||||
if (this->sign.kdtree_valid) _viewport_sign_kdtree.Remove(ViewportSignKdtreeItem::MakeWaypoint(this->index));
|
||||
|
||||
SetDParam(0, this->index);
|
||||
this->sign.UpdatePosition(pt.x, pt.y - 32 * ZOOM_BASE, STR_VIEWPORT_WAYPOINT);
|
||||
this->sign.UpdatePosition(pt.x, pt.y - 32 * ZOOM_BASE, STR_WAYPOINT_NAME);
|
||||
|
||||
_viewport_sign_kdtree.Insert(ViewportSignKdtreeItem::MakeWaypoint(this->index));
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user