Merge branch 'master' into enhanced_viewport_overlay
Notes on conflict resolution: * MarkTileDirtyByTile gained an extra param on both sides of the merge Move bridge level offset to be after zoom level param, as it's used less. * Add zoom level params to MarkBridgeDirty functions * Fix undefined behaviour in colour_index cycling in ViewportMapDraw Conflicts: src/clear_cmd.cpp src/pbs.cpp src/rail_cmd.cpp src/toolbar_gui.cpp src/train_cmd.cpp src/vehicle.cpp src/viewport.cpp src/viewport_func.h
This commit is contained in:
@@ -15,9 +15,11 @@
|
||||
* functions may still be available if you return an older API version
|
||||
* in GetAPIVersion() in info.nut.
|
||||
*
|
||||
* \b 1.5.0
|
||||
* \b 1.6.0
|
||||
*
|
||||
* 1.5.0 is not yet released. The following changes are not set in stone yet.
|
||||
* 1.6.0 is not yet released. The following changes are not set in stone yet.
|
||||
*
|
||||
* \b 1.5.0
|
||||
*
|
||||
* API additions:
|
||||
* \li AIList::SwapList
|
||||
|
@@ -28,8 +28,13 @@ void SQGSNews_Register(Squirrel *engine)
|
||||
SQGSNews.DefSQConst(engine, ScriptNews::NT_ACCEPTANCE, "NT_ACCEPTANCE");
|
||||
SQGSNews.DefSQConst(engine, ScriptNews::NT_SUBSIDIES, "NT_SUBSIDIES");
|
||||
SQGSNews.DefSQConst(engine, ScriptNews::NT_GENERAL, "NT_GENERAL");
|
||||
SQGSNews.DefSQConst(engine, ScriptNews::NR_NONE, "NR_NONE");
|
||||
SQGSNews.DefSQConst(engine, ScriptNews::NR_TILE, "NR_TILE");
|
||||
SQGSNews.DefSQConst(engine, ScriptNews::NR_STATION, "NR_STATION");
|
||||
SQGSNews.DefSQConst(engine, ScriptNews::NR_INDUSTRY, "NR_INDUSTRY");
|
||||
SQGSNews.DefSQConst(engine, ScriptNews::NR_TOWN, "NR_TOWN");
|
||||
|
||||
SQGSNews.DefSQStaticMethod(engine, &ScriptNews::Create, "Create", 4, ".i.i");
|
||||
SQGSNews.DefSQStaticMethod(engine, &ScriptNews::Create, "Create", 6, ".i.iii");
|
||||
|
||||
SQGSNews.PostRegister(engine);
|
||||
}
|
||||
|
@@ -774,10 +774,12 @@ void SQGSWindow_Register(Squirrel *engine)
|
||||
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_SA_RIGHT, "WID_SA_RIGHT");
|
||||
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_SA_DOWN, "WID_SA_DOWN");
|
||||
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_SA_SPRITE, "WID_SA_SPRITE");
|
||||
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_SA_OFFSETS, "WID_SA_OFFSETS");
|
||||
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_SA_OFFSETS_ABS, "WID_SA_OFFSETS_ABS");
|
||||
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_SA_OFFSETS_REL, "WID_SA_OFFSETS_REL");
|
||||
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_SA_PICKER, "WID_SA_PICKER");
|
||||
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_SA_LIST, "WID_SA_LIST");
|
||||
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_SA_SCROLLBAR, "WID_SA_SCROLLBAR");
|
||||
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_SA_RESET_REL, "WID_SA_RESET_REL");
|
||||
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_NP_SHOW_NUMPAR, "WID_NP_SHOW_NUMPAR");
|
||||
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_NP_NUMPAR_DEC, "WID_NP_NUMPAR_DEC");
|
||||
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_NP_NUMPAR_INC, "WID_NP_NUMPAR_INC");
|
||||
@@ -1004,6 +1006,7 @@ void SQGSWindow_Register(Squirrel *engine)
|
||||
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_GO_LANG_DROPDOWN, "WID_GO_LANG_DROPDOWN");
|
||||
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_GO_RESOLUTION_DROPDOWN, "WID_GO_RESOLUTION_DROPDOWN");
|
||||
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_GO_FULLSCREEN_BUTTON, "WID_GO_FULLSCREEN_BUTTON");
|
||||
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_GO_GUI_ZOOM_DROPDOWN, "WID_GO_GUI_ZOOM_DROPDOWN");
|
||||
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_GO_BASE_GRF_DROPDOWN, "WID_GO_BASE_GRF_DROPDOWN");
|
||||
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_GO_BASE_GRF_STATUS, "WID_GO_BASE_GRF_STATUS");
|
||||
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_GO_BASE_GRF_TEXTFILE, "WID_GO_BASE_GRF_TEXTFILE");
|
||||
|
@@ -15,9 +15,11 @@
|
||||
* functions may still be available if you return an older API version
|
||||
* in GetAPIVersion() in info.nut.
|
||||
*
|
||||
* \b 1.5.0
|
||||
* \b 1.6.0
|
||||
*
|
||||
* 1.5.0 is not yet released. The following changes are not set in stone yet.
|
||||
* 1.6.0 is not yet released. The following changes are not set in stone yet.
|
||||
*
|
||||
* \b 1.5.0
|
||||
*
|
||||
* API additions:
|
||||
* \li GSList::SwapList
|
||||
@@ -35,6 +37,11 @@
|
||||
* \li GSStationList_CargoWaitingFromByVia
|
||||
* \li GSStationList_CargoWaitingViaByFrom
|
||||
*
|
||||
* Other changes:
|
||||
* \li GSNews::Create takes two extra parameters to refer to a location, station,
|
||||
* industry, or town. The user can click at the news message to jump to the
|
||||
* referred location.
|
||||
*
|
||||
* \b 1.4.4
|
||||
*
|
||||
* No changes
|
||||
|
@@ -90,8 +90,15 @@ public:
|
||||
* @param cargo_type The cargo to get the string representation of.
|
||||
* @pre ScriptCargo::IsValidCargo(cargo_type).
|
||||
* @return The cargo label.
|
||||
* @note Never use this to check if it is a certain cargo. NewGRF can
|
||||
* redefine all of the names.
|
||||
* @note
|
||||
* - The label uniquely identifies a specific cargo. Use this if you want to
|
||||
* detect special cargos from specific industry set (like production booster cargos, supplies, ...).
|
||||
* - For more generic cargo support, rather check cargo properties though. For example:
|
||||
* - Use ScriptCargo::HasCargoClass(..., CC_PASSENGER) to decide bus vs. truck requirements.
|
||||
* - Use ScriptCargo::GetTownEffect(...) paired with ScriptTown::GetCargoGoal(...) to determine
|
||||
* town growth requirements.
|
||||
* - In other words: Only use the cargo label, if you know more about the behaviour
|
||||
* of a specific cargo from a specific industry set, than the API methods can tell you.
|
||||
*/
|
||||
static char *GetCargoLabel(CargoID cargo_type);
|
||||
|
||||
|
@@ -440,24 +440,25 @@ void ScriptList::AddItem(int64 item, int64 value)
|
||||
|
||||
if (this->HasItem(item)) return;
|
||||
|
||||
this->items[item] = 0;
|
||||
this->buckets[0].insert(item);
|
||||
|
||||
this->SetValue(item, value);
|
||||
this->items[item] = value;
|
||||
this->buckets[value].insert(item);
|
||||
}
|
||||
|
||||
void ScriptList::RemoveItem(int64 item)
|
||||
{
|
||||
this->modifications++;
|
||||
|
||||
if (!this->HasItem(item)) return;
|
||||
ScriptListMap::iterator item_iter = this->items.find(item);
|
||||
if (item_iter == this->items.end()) return;
|
||||
|
||||
int64 value = this->GetValue(item);
|
||||
int64 value = item_iter->second;
|
||||
|
||||
this->sorter->Remove(item);
|
||||
this->buckets[value].erase(item);
|
||||
if (this->buckets[value].empty()) this->buckets.erase(value);
|
||||
this->items.erase(item);
|
||||
ScriptListBucket::iterator bucket_iter = this->buckets.find(value);
|
||||
assert(bucket_iter != this->buckets.end());
|
||||
bucket_iter->second.erase(item);
|
||||
if (bucket_iter->second.empty()) this->buckets.erase(bucket_iter);
|
||||
this->items.erase(item_iter);
|
||||
}
|
||||
|
||||
int64 ScriptList::Begin()
|
||||
@@ -496,24 +497,26 @@ int32 ScriptList::Count()
|
||||
|
||||
int64 ScriptList::GetValue(int64 item)
|
||||
{
|
||||
if (!this->HasItem(item)) return 0;
|
||||
|
||||
return this->items[item];
|
||||
ScriptListMap::const_iterator item_iter = this->items.find(item);
|
||||
return item_iter == this->items.end() ? 0 : item_iter->second;
|
||||
}
|
||||
|
||||
bool ScriptList::SetValue(int64 item, int64 value)
|
||||
{
|
||||
this->modifications++;
|
||||
|
||||
if (!this->HasItem(item)) return false;
|
||||
ScriptListMap::iterator item_iter = this->items.find(item);
|
||||
if (item_iter == this->items.end()) return false;
|
||||
|
||||
int64 value_old = this->GetValue(item);
|
||||
int64 value_old = item_iter->second;
|
||||
if (value_old == value) return true;
|
||||
|
||||
this->sorter->Remove(item);
|
||||
this->buckets[value_old].erase(item);
|
||||
if (this->buckets[value_old].empty()) this->buckets.erase(value_old);
|
||||
this->items[item] = value;
|
||||
ScriptListBucket::iterator bucket_iter = this->buckets.find(value_old);
|
||||
assert(bucket_iter != this->buckets.end());
|
||||
bucket_iter->second.erase(item);
|
||||
if (bucket_iter->second.empty()) this->buckets.erase(bucket_iter);
|
||||
item_iter->second = value;
|
||||
this->buckets[value].insert(item);
|
||||
|
||||
return true;
|
||||
@@ -553,6 +556,8 @@ void ScriptList::Sort(SorterType sorter, bool ascending)
|
||||
|
||||
void ScriptList::AddList(ScriptList *list)
|
||||
{
|
||||
if (list == this) return;
|
||||
|
||||
ScriptListMap *list_items = &list->items;
|
||||
for (ScriptListMap::iterator iter = list_items->begin(); iter != list_items->end(); iter++) {
|
||||
this->AddItem((*iter).first);
|
||||
@@ -562,6 +567,8 @@ void ScriptList::AddList(ScriptList *list)
|
||||
|
||||
void ScriptList::SwapList(ScriptList *list)
|
||||
{
|
||||
if (list == this) return;
|
||||
|
||||
this->items.swap(list->items);
|
||||
this->buckets.swap(list->buckets);
|
||||
Swap(this->sorter, list->sorter);
|
||||
@@ -691,9 +698,13 @@ void ScriptList::RemoveList(ScriptList *list)
|
||||
{
|
||||
this->modifications++;
|
||||
|
||||
ScriptListMap *list_items = &list->items;
|
||||
for (ScriptListMap::iterator iter = list_items->begin(); iter != list_items->end(); iter++) {
|
||||
this->RemoveItem((*iter).first);
|
||||
if (list == this) {
|
||||
Clear();
|
||||
} else {
|
||||
ScriptListMap *list_items = &list->items;
|
||||
for (ScriptListMap::iterator iter = list_items->begin(); iter != list_items->end(); iter++) {
|
||||
this->RemoveItem((*iter).first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -753,14 +764,12 @@ void ScriptList::KeepBottom(int32 count)
|
||||
|
||||
void ScriptList::KeepList(ScriptList *list)
|
||||
{
|
||||
if (list == this) return;
|
||||
|
||||
this->modifications++;
|
||||
|
||||
ScriptList tmp;
|
||||
for (ScriptListMap::iterator iter = this->items.begin(); iter != this->items.end(); iter++) {
|
||||
tmp.AddItem((*iter).first);
|
||||
tmp.SetValue((*iter).first, (*iter).second);
|
||||
}
|
||||
|
||||
tmp.AddList(this);
|
||||
tmp.RemoveList(list);
|
||||
this->RemoveList(&tmp);
|
||||
}
|
||||
@@ -772,9 +781,10 @@ SQInteger ScriptList::_get(HSQUIRRELVM vm)
|
||||
SQInteger idx;
|
||||
sq_getinteger(vm, 2, &idx);
|
||||
|
||||
if (!this->HasItem(idx)) return SQ_ERROR;
|
||||
ScriptListMap::const_iterator item_iter = this->items.find(idx);
|
||||
if (item_iter == this->items.end()) return SQ_ERROR;
|
||||
|
||||
sq_pushinteger(vm, this->GetValue(idx));
|
||||
sq_pushinteger(vm, item_iter->second);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@@ -11,13 +11,17 @@
|
||||
|
||||
#include "../../stdafx.h"
|
||||
#include "script_news.hpp"
|
||||
#include "script_industry.hpp"
|
||||
#include "script_station.hpp"
|
||||
#include "script_map.hpp"
|
||||
#include "script_town.hpp"
|
||||
#include "script_error.hpp"
|
||||
#include "../../command_type.h"
|
||||
#include "../../string_func.h"
|
||||
|
||||
#include "../../safeguards.h"
|
||||
|
||||
/* static */ bool ScriptNews::Create(NewsType type, Text *text, ScriptCompany::CompanyID company)
|
||||
/* static */ bool ScriptNews::Create(NewsType type, Text *text, ScriptCompany::CompanyID company, NewsReferenceType ref_type, uint32 reference)
|
||||
{
|
||||
CCountedPtr<Text> counter(text);
|
||||
|
||||
@@ -26,9 +30,15 @@
|
||||
EnforcePreconditionEncodedText(false, encoded);
|
||||
EnforcePrecondition(false, type == NT_ECONOMY || type == NT_SUBSIDIES || type == NT_GENERAL);
|
||||
EnforcePrecondition(false, company == ScriptCompany::COMPANY_INVALID || ScriptCompany::ResolveCompanyID(company) != ScriptCompany::COMPANY_INVALID);
|
||||
EnforcePrecondition(false, (ref_type == NR_NONE) ||
|
||||
(ref_type == NR_TILE && ScriptMap::IsValidTile(reference)) ||
|
||||
(ref_type == NR_STATION && ScriptStation::IsValidStation(reference)) ||
|
||||
(ref_type == NR_INDUSTRY && ScriptIndustry::IsValidIndustry(reference)) ||
|
||||
(ref_type == NR_TOWN && ScriptTown::IsValidTown(reference)));
|
||||
|
||||
uint8 c = company;
|
||||
if (company == ScriptCompany::COMPANY_INVALID) c = INVALID_COMPANY;
|
||||
|
||||
return ScriptObject::DoCommand(0, type | (NR_NONE << 8) | (c << 16), 0, CMD_CUSTOM_NEWS_ITEM, encoded);
|
||||
if (ref_type == NR_NONE) reference = 0;
|
||||
return ScriptObject::DoCommand(0, type | (ref_type << 8) | (c << 16), reference, CMD_CUSTOM_NEWS_ITEM, encoded);
|
||||
}
|
||||
|
@@ -36,15 +36,36 @@ public:
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a news messages for a company.
|
||||
* Reference to a game element.
|
||||
*/
|
||||
enum NewsReferenceType {
|
||||
/* Selection of useful game elements to refer to. */
|
||||
NR_NONE = ::NR_NONE, ///< No reference supplied.
|
||||
NR_TILE = ::NR_TILE, ///< Reference location, scroll to the location when clicking on the news.
|
||||
NR_STATION = ::NR_STATION, ///< Reference station, scroll to the station when clicking on the news. Delete news when the station is deleted.
|
||||
NR_INDUSTRY = ::NR_INDUSTRY, ///< Reference industry, scrolls to the industry when clicking on the news. Delete news when the industry is deleted.
|
||||
NR_TOWN = ::NR_TOWN, ///< Reference town, scroll to the town when clicking on the news.
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a news message for everybody, or for one company.
|
||||
* @param type The type of the news.
|
||||
* @param text The text message to show (can be either a raw string, or a ScriptText object).
|
||||
* @param company The company, or COMPANY_INVALID for all companies.
|
||||
* @param ref_type Type of referred game element.
|
||||
* @param reference The referenced game element of \a ref_type.
|
||||
* - For #NR_NONE this parameter is ignored.
|
||||
* - For #NR_TILE this parameter should be a valid location (ScriptMap::IsValidTile).
|
||||
* - For #NR_STATION this parameter should be a valid stationID (ScriptStation::IsValidStation).
|
||||
* - For #NR_INDUSTRY this parameter should be a valid industryID (ScriptIndustry::IsValidIndustry).
|
||||
* - For #NR_TOWN this parameter should be a valid townID (ScriptTown::IsValidTown).
|
||||
* @return True if the action succeeded.
|
||||
* @pre type must be #NT_ECONOMY, #NT_SUBSIDIES, or #NT_GENERAL.
|
||||
* @pre text != NULL.
|
||||
* @pre company == COMPANY_INVALID || ResolveCompanyID(company) != COMPANY_INVALID.
|
||||
* @pre The \a reference condition must be fulfilled.
|
||||
*/
|
||||
static bool Create(NewsType type, Text *text, ScriptCompany::CompanyID company);
|
||||
static bool Create(NewsType type, Text *text, ScriptCompany::CompanyID company, NewsReferenceType ref_type, uint32 reference);
|
||||
};
|
||||
|
||||
#endif /* SCRIPT_NEWS_HPP */
|
||||
|
@@ -65,9 +65,10 @@ class ScriptTileList_IndustryAccepting : public ScriptTileList {
|
||||
public:
|
||||
/**
|
||||
* @param industry_id The industry to create the ScriptTileList around.
|
||||
* @param radius The radius of the station you will be using.
|
||||
* @param radius The coverage radius of the station type you will be using.
|
||||
* @pre ScriptIndustry::IsValidIndustry(industry_id).
|
||||
* @pre radius > 0.
|
||||
* @note A station part built on any of the returned tiles will give you coverage.
|
||||
*/
|
||||
ScriptTileList_IndustryAccepting(IndustryID industry_id, int radius);
|
||||
};
|
||||
@@ -82,9 +83,10 @@ class ScriptTileList_IndustryProducing : public ScriptTileList {
|
||||
public:
|
||||
/**
|
||||
* @param industry_id The industry to create the ScriptTileList around.
|
||||
* @param radius The radius of the station you will be using.
|
||||
* @param radius The coverage radius of the station type you will be using.
|
||||
* @pre ScriptIndustry::IsValidIndustry(industry_id).
|
||||
* @pre radius > 0.
|
||||
* @note A station part built on any of the returned tiles will give you acceptance.
|
||||
*/
|
||||
ScriptTileList_IndustryProducing(IndustryID industry_id, int radius);
|
||||
};
|
||||
|
@@ -529,7 +529,7 @@ public:
|
||||
/**
|
||||
* Generate landscape (newgame); %Window numbers:
|
||||
* - GLWM_SCENARIO = #CreateScenarioWidgets
|
||||
* - #GenenerateLandscapeWindowMode = #GenerateLandscapeWidgets
|
||||
* - #GenerateLandscapeWindowMode = #GenerateLandscapeWidgets
|
||||
*/
|
||||
WC_GENERATE_LANDSCAPE = ::WC_GENERATE_LANDSCAPE,
|
||||
|
||||
@@ -1800,10 +1800,12 @@ public:
|
||||
WID_SA_RIGHT = ::WID_SA_RIGHT, ///< Move the sprite to the right.
|
||||
WID_SA_DOWN = ::WID_SA_DOWN, ///< Move the sprite down.
|
||||
WID_SA_SPRITE = ::WID_SA_SPRITE, ///< The actual sprite.
|
||||
WID_SA_OFFSETS = ::WID_SA_OFFSETS, ///< The sprite offsets.
|
||||
WID_SA_OFFSETS_ABS = ::WID_SA_OFFSETS_ABS, ///< The sprite offsets (absolute).
|
||||
WID_SA_OFFSETS_REL = ::WID_SA_OFFSETS_REL, ///< The sprite offsets (relative).
|
||||
WID_SA_PICKER = ::WID_SA_PICKER, ///< Sprite picker.
|
||||
WID_SA_LIST = ::WID_SA_LIST, ///< Queried sprite list.
|
||||
WID_SA_SCROLLBAR = ::WID_SA_SCROLLBAR, ///< Scrollbar for sprite list.
|
||||
WID_SA_RESET_REL = ::WID_SA_RESET_REL, ///< Reset relative sprite offset
|
||||
};
|
||||
|
||||
/* automatically generated from ../../widgets/newgrf_widget.h */
|
||||
@@ -2128,6 +2130,7 @@ public:
|
||||
WID_GO_LANG_DROPDOWN = ::WID_GO_LANG_DROPDOWN, ///< Language dropdown.
|
||||
WID_GO_RESOLUTION_DROPDOWN = ::WID_GO_RESOLUTION_DROPDOWN, ///< Dropdown for the resolution.
|
||||
WID_GO_FULLSCREEN_BUTTON = ::WID_GO_FULLSCREEN_BUTTON, ///< Toggle fullscreen.
|
||||
WID_GO_GUI_ZOOM_DROPDOWN = ::WID_GO_GUI_ZOOM_DROPDOWN, ///< Dropdown for the GUI zoom level.
|
||||
WID_GO_BASE_GRF_DROPDOWN = ::WID_GO_BASE_GRF_DROPDOWN, ///< Use to select a base GRF.
|
||||
WID_GO_BASE_GRF_STATUS = ::WID_GO_BASE_GRF_STATUS, ///< Info about missing files etc.
|
||||
WID_GO_BASE_GRF_TEXTFILE = ::WID_GO_BASE_GRF_TEXTFILE, ///< Open base GRF readme, changelog (+1) or license (+2).
|
||||
|
@@ -15,6 +15,8 @@ namespace SQConvert {
|
||||
/* Allow enums to be used as Squirrel parameters */
|
||||
template <> inline ScriptNews::NewsType GetParam(ForceType<ScriptNews::NewsType>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (ScriptNews::NewsType)tmp; }
|
||||
template <> inline int Return<ScriptNews::NewsType>(HSQUIRRELVM vm, ScriptNews::NewsType res) { sq_pushinteger(vm, (int32)res); return 1; }
|
||||
template <> inline ScriptNews::NewsReferenceType GetParam(ForceType<ScriptNews::NewsReferenceType>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (ScriptNews::NewsReferenceType)tmp; }
|
||||
template <> inline int Return<ScriptNews::NewsReferenceType>(HSQUIRRELVM vm, ScriptNews::NewsReferenceType res) { sq_pushinteger(vm, (int32)res); return 1; }
|
||||
|
||||
/* Allow ScriptNews to be used as Squirrel parameter */
|
||||
template <> inline ScriptNews *GetParam(ForceType<ScriptNews *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (ScriptNews *)instance; }
|
||||
|
Reference in New Issue
Block a user