mirror of
https://github.com/OpenRCT2/OpenRCT2
synced 2025-12-24 08:12:53 +01:00
implement new campaign window
This commit is contained in:
@@ -70,7 +70,7 @@ enum GAME_COMMAND {
|
||||
GAME_COMMAND_SET_CURRENT_LOAN,
|
||||
GAME_COMMAND_46,
|
||||
GAME_COMMAND_47,
|
||||
GAME_COMMAND_48,
|
||||
GAME_COMMAND_START_MARKETING_CAMPAIGN,
|
||||
GAME_COMMAND_49,
|
||||
GAME_COMMAND_50,
|
||||
GAME_COMMAND_51,
|
||||
|
||||
@@ -587,6 +587,11 @@ enum {
|
||||
STR_MARKETING_CAMPAIGNS_AVAILABLE = 2411,
|
||||
STR_START_THIS_MARKETING_CAMPAIGN = 2412,
|
||||
STR_MARKETING_PER_WEEK = 2413,
|
||||
STR_MARKETING_NOT_SELECTED = 2414,
|
||||
STR_MARKETING_RIDE = 2415,
|
||||
STR_MARKETING_ITEM = 2416,
|
||||
|
||||
STR_LENGTH_OF_TIME = 2417,
|
||||
|
||||
STR_MARKETING_VOUCHERS_FOR_FREE_ENTRY_TO_THE_PARK = 2424,
|
||||
STR_MARKETING_VOUCHERS_FOR_FREE_RIDES_ON_A_PARTICULAR_RIDE = 2425,
|
||||
@@ -733,6 +738,7 @@ enum {
|
||||
|
||||
STR_TUTORIAL = 2856,
|
||||
STR_PRESS_KEY_OR_MOUSE_BUTTON_FOR_CONTROL = 2857,
|
||||
STR_CANT_START_MARKETING_CAMPAIGN = 2858,
|
||||
|
||||
STR_INFOGRAMES_INTERACTIVE_CREDITS = 2860,
|
||||
STR_LICENSED_TO_INFOGRAMES_INTERACTIVE_INC = 2861,
|
||||
|
||||
@@ -157,8 +157,6 @@ void widget_draw(rct_drawpixelinfo *dpi, rct_window *w, int widgetIndex)
|
||||
widget_scroll_draw(dpi, w, widgetIndex);
|
||||
break;
|
||||
case WWT_CHECKBOX:
|
||||
widget_checkbox_draw(dpi, w, widgetIndex);
|
||||
break;
|
||||
case WWT_24:
|
||||
widget_checkbox_draw(dpi, w, widgetIndex);
|
||||
break;
|
||||
@@ -414,9 +412,9 @@ static void widget_text_unknown(rct_drawpixelinfo *dpi, rct_window *w, int widge
|
||||
widget = &w->widgets[widgetIndex];
|
||||
|
||||
// Get the colour
|
||||
// colour = w->colours[widget->colour];
|
||||
colour = w->colours[widget->colour];
|
||||
// do not use widget color as this is already used as background for the text_button
|
||||
colour = 2;
|
||||
// colour = 2;
|
||||
|
||||
// Resolve the absolute ltrb
|
||||
l = w->x + widget->left;
|
||||
|
||||
10
src/window.h
10
src/window.h
@@ -394,6 +394,11 @@ void window_event_helper(rct_window* w, short widgetIndex, WINDOW_EVENTS event);
|
||||
__asm mov widgetIndex, dx \
|
||||
__asm mov w, esi
|
||||
|
||||
#define window_dropdown_get_registers(w, widgetIndex, dropdownIndex) \
|
||||
__asm mov dropdownIndex, ax \
|
||||
__asm mov widgetIndex, dx \
|
||||
__asm mov w, esi
|
||||
|
||||
#define window_paint_get_registers(w, dpi) \
|
||||
__asm mov w, esi \
|
||||
__asm mov dpi, edi
|
||||
@@ -405,6 +410,11 @@ void window_event_helper(rct_window* w, short widgetIndex, WINDOW_EVENTS event);
|
||||
__asm__ ( "mov %[widgetIndex], dx " : [widgetIndex] "+m" (widgetIndex) ); \
|
||||
__asm__ ( "mov %[w], esi " : [w] "+m" (w) );
|
||||
|
||||
#define window_dropdown_get_registers(w, widgetIndex, dropdownIndex) \
|
||||
__asm__ ( "mov %[dropdownIndex], ax " : [dropdownIndex] "+m" (dropdownIndex) ); \
|
||||
__asm__ ( "mov %[widgetIndex], dx " : [widgetIndex] "+m" (widgetIndex) ); \
|
||||
__asm__ ( "mov %[w], esi " : [w] "+m" (w) );
|
||||
|
||||
#define window_paint_get_registers(w, dpi) \
|
||||
__asm__ ( "mov %[w], esi " : [w] "+m" (w) ); \
|
||||
__asm__ ( "mov %[dpi], edi " : [dpi] "+m" (dpi) );
|
||||
|
||||
@@ -18,8 +18,111 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*****************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "addresses.h"
|
||||
#include "game.h"
|
||||
#include "marketing.h"
|
||||
#include "ride.h"
|
||||
#include "string_ids.h"
|
||||
#include "widget.h"
|
||||
#include "window.h"
|
||||
#include "window_dropdown.h"
|
||||
|
||||
#define SELECTED_RIDE_UNDEFINED ((sint16)0xFFFF)
|
||||
|
||||
enum WINDOW_NEW_CAMPAIGN_WIDGET_IDX {
|
||||
WIDX_BACKGROUND,
|
||||
WIDX_TITLE,
|
||||
WIDX_CLOSE,
|
||||
WIDX_RIDE_LABEL,
|
||||
WIDX_RIDE_DROPDOWN,
|
||||
WIDX_RIDE_DROPDOWN_BUTTON,
|
||||
WIDX_WEEKS_LABEL,
|
||||
WIDX_WEEKS_SPINNER,
|
||||
WIDX_WEEKS_INCREASE_BUTTON,
|
||||
WIDX_WEEKS_DECREASE_BUTTON,
|
||||
WIDX_START_BUTTON
|
||||
};
|
||||
|
||||
static rct_widget window_new_campaign_widgets[] = {
|
||||
{ WWT_FRAME, 0, 0, 349, 0, 106, 0xFFFFFFFF, STR_NONE }, // panel / background
|
||||
{ WWT_CAPTION, 0, 1, 348, 1, 14, 0, STR_WINDOW_TITLE_TIP }, // title bar
|
||||
{ WWT_CLOSEBOX, 0, 337, 347, 2, 13, STR_CLOSE_X, STR_CLOSE_WINDOW_TIP }, // close x button
|
||||
{ WWT_24, 0, 0, 139, 24, 35, 0, STR_NONE }, // ride label
|
||||
{ WWT_DROPDOWN, 0, 100, 341, 24, 35, 0, STR_NONE }, // ride dropdown
|
||||
{ WWT_DROPDOWN_BUTTON, 0, 330, 340, 25, 34, 876, STR_NONE }, // ride dropdown button
|
||||
{ WWT_24, 0, 0, 139, 41, 52, STR_LENGTH_OF_TIME, STR_NONE }, // weeks label
|
||||
{ WWT_SPINNER, 0, 120, 219, 41, 52, 0, STR_NONE }, // weeks
|
||||
{ WWT_DROPDOWN_BUTTON, 0, 208, 218, 42, 46, STR_NUMERIC_UP, STR_NONE }, // weeks +
|
||||
{ WWT_DROPDOWN_BUTTON, 0, 208, 218, 47, 51, STR_NUMERIC_DOWN, STR_NONE }, // weeks -
|
||||
{ WWT_DROPDOWN_BUTTON, 0, 14, 335, 89, 100, STR_MARKETING_START_THIS_MARKETING_CAMPAIGN, STR_NONE }, // start button
|
||||
{ WIDGETS_END }
|
||||
};
|
||||
|
||||
|
||||
static void window_new_campaign_emptysub() { }
|
||||
static void window_new_campaign_mouseup();
|
||||
static void window_new_campaign_mousedown(int widgetIndex, rct_window*w, rct_widget* widget);
|
||||
static void window_new_campaign_dropdown();
|
||||
static void window_new_campaign_invalidate();
|
||||
static void window_new_campaign_paint();
|
||||
|
||||
static void* window_new_campaign_events[] = {
|
||||
window_new_campaign_emptysub,
|
||||
window_new_campaign_mouseup,
|
||||
window_new_campaign_emptysub,
|
||||
window_new_campaign_mousedown,
|
||||
window_new_campaign_dropdown,
|
||||
window_new_campaign_emptysub,
|
||||
window_new_campaign_emptysub,
|
||||
window_new_campaign_emptysub,
|
||||
window_new_campaign_emptysub,
|
||||
window_new_campaign_emptysub,
|
||||
window_new_campaign_emptysub,
|
||||
window_new_campaign_emptysub,
|
||||
window_new_campaign_emptysub,
|
||||
window_new_campaign_emptysub,
|
||||
window_new_campaign_emptysub,
|
||||
window_new_campaign_emptysub,
|
||||
window_new_campaign_emptysub,
|
||||
window_new_campaign_emptysub,
|
||||
window_new_campaign_emptysub,
|
||||
window_new_campaign_emptysub,
|
||||
window_new_campaign_emptysub,
|
||||
window_new_campaign_emptysub,
|
||||
window_new_campaign_emptysub,
|
||||
window_new_campaign_emptysub,
|
||||
window_new_campaign_emptysub,
|
||||
window_new_campaign_invalidate,
|
||||
window_new_campaign_paint,
|
||||
window_new_campaign_emptysub
|
||||
};
|
||||
|
||||
uint8 window_new_campaign_rides[MAX_RIDES];
|
||||
uint8 window_new_campaign_shop_items[64];
|
||||
|
||||
int ride_reliability_compare(const void *a, const void *b)
|
||||
{
|
||||
rct_ride *rideA, *rideB;
|
||||
|
||||
rideA = GET_RIDE(*((uint8*)a));
|
||||
rideB = GET_RIDE(*((uint8*)b));
|
||||
return rideB->reliability - rideA->reliability;
|
||||
}
|
||||
|
||||
int ride_name_compare(const void *a, const void *b)
|
||||
{
|
||||
char rideAName[256], rideBName[256];
|
||||
rct_ride *rideA, *rideB;
|
||||
|
||||
rideA = GET_RIDE(*((uint8*)a));
|
||||
rideB = GET_RIDE(*((uint8*)b));
|
||||
|
||||
format_string(rideAName, rideA->var_04A, &rideA->var_04C);
|
||||
format_string(rideBName, rideB->var_04A, &rideB->var_04C);
|
||||
|
||||
return _strcmpi(rideAName, rideBName);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -27,5 +130,290 @@
|
||||
*/
|
||||
void window_new_campaign_open(int campaignType)
|
||||
{
|
||||
RCT2_CALLPROC_X(0x0069E16F, campaignType, 0, 0, 0, 0, 0, 0);
|
||||
// RCT2_CALLPROC_X(0x0069E16F, campaignType, 0, 0, 0, 0, 0, 0);
|
||||
|
||||
rct_window *w;
|
||||
rct_ride *ride;
|
||||
int i, numApplicableRides;
|
||||
|
||||
w = window_bring_to_front_by_id(WC_NEW_CAMPAIGN, 0);
|
||||
if (w != NULL) {
|
||||
if (w->var_480 == campaignType)
|
||||
return;
|
||||
|
||||
window_close(w);
|
||||
}
|
||||
|
||||
w = window_create_auto_pos(350, 107, (uint32*)window_new_campaign_events, WC_NEW_CAMPAIGN, 0);
|
||||
w->widgets = window_new_campaign_widgets;
|
||||
w->enabled_widgets =
|
||||
(1 << WIDX_CLOSE) |
|
||||
(1 << WIDX_RIDE_DROPDOWN) |
|
||||
(1 << WIDX_RIDE_DROPDOWN_BUTTON) |
|
||||
(1 << WIDX_WEEKS_INCREASE_BUTTON) |
|
||||
(1 << WIDX_WEEKS_DECREASE_BUTTON) |
|
||||
(1 << WIDX_START_BUTTON);
|
||||
w->var_020 = 0x300;
|
||||
window_init_scroll_widgets(w);
|
||||
w->colours[0] = 19;
|
||||
w->colours[1] = 19;
|
||||
w->colours[2] = 19;
|
||||
|
||||
window_new_campaign_widgets[WIDX_TITLE].image = STR_MARKETING_VOUCHERS_FOR_FREE_ENTRY_TO_THE_PARK + campaignType;
|
||||
|
||||
// Campaign type
|
||||
w->var_480 = campaignType;
|
||||
|
||||
// Number of weeks
|
||||
w->var_482 = 2;
|
||||
|
||||
// Currently selected ride
|
||||
w->var_484 = SELECTED_RIDE_UNDEFINED;
|
||||
|
||||
// Get all applicable rides
|
||||
numApplicableRides = 0;
|
||||
window_new_campaign_rides[0] = 255;
|
||||
FOR_ALL_RIDES(i, ride) {
|
||||
if (!(RCT2_GLOBAL(0x0097CF40 + (ride->type * 8), uint32) & 0x03820000))
|
||||
window_new_campaign_rides[numApplicableRides++] = i;
|
||||
}
|
||||
window_new_campaign_rides[numApplicableRides] = 255;
|
||||
|
||||
// Take top 40 most reliable rides
|
||||
if (numApplicableRides > 40) {
|
||||
qsort(window_new_campaign_rides, countof(window_new_campaign_rides), sizeof(uint8), ride_reliability_compare);
|
||||
numApplicableRides = 40;
|
||||
}
|
||||
|
||||
// Sort rides by name
|
||||
qsort(window_new_campaign_rides, numApplicableRides, sizeof(uint8), ride_name_compare);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* rct2: 0x0069E320
|
||||
*/
|
||||
static void window_new_campaign_get_shop_items()
|
||||
{
|
||||
int i, numItems;
|
||||
rct_ride *ride;
|
||||
|
||||
uint64 items = 0;
|
||||
FOR_ALL_RIDES(i, ride) {
|
||||
uint8 *rideTypeInfo = RCT2_ADDRESS(0x009ACFA4, void*)[ride->subtype];
|
||||
uint8 itemType = RCT2_GLOBAL(rideTypeInfo + 0x1C0, uint8);
|
||||
if (itemType != 255)
|
||||
items |= 1LL << itemType;
|
||||
}
|
||||
|
||||
// Remove certain items?
|
||||
items &= 0x0011FF78036BA3E0;
|
||||
|
||||
//
|
||||
numItems = 0;
|
||||
for (i = 0; i < 64; i++)
|
||||
if (items & (1LL << i))
|
||||
window_new_campaign_shop_items[numItems++] = i;
|
||||
window_new_campaign_shop_items[numItems] = 255;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* rct2: 0x0069E50B
|
||||
*/
|
||||
static void window_new_campaign_mouseup()
|
||||
{
|
||||
rct_window *w;
|
||||
short widgetIndex;
|
||||
|
||||
window_mouse_up_get_registers(w, widgetIndex);
|
||||
|
||||
switch (widgetIndex) {
|
||||
case WIDX_CLOSE:
|
||||
window_close(w);
|
||||
break;
|
||||
case WIDX_START_BUTTON:
|
||||
RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_TITLE, uint16) = STR_CANT_START_MARKETING_CAMPAIGN;
|
||||
game_do_command(0, (w->var_482 << 8) | 1, 0, (w->var_484 << 8) | w->var_480, GAME_COMMAND_START_MARKETING_CAMPAIGN, 0, 0);
|
||||
window_close(w);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* rct2: 0x0069E51C
|
||||
*/
|
||||
static void window_new_campaign_mousedown(int widgetIndex, rct_window *w, rct_widget* widget)
|
||||
{
|
||||
rct_widget *dropdownWidget;
|
||||
|
||||
switch (widgetIndex) {
|
||||
case WIDX_RIDE_DROPDOWN_BUTTON:
|
||||
dropdownWidget = widget - 1;
|
||||
|
||||
if (w->var_480 == ADVERTISING_CAMPAIGN_FOOD_OR_DRINK_FREE) {
|
||||
window_new_campaign_get_shop_items();
|
||||
if (window_new_campaign_shop_items[0] != 255) {
|
||||
int numItems = 0;
|
||||
for (int i = 0; i < 40; i++) {
|
||||
if (window_new_campaign_shop_items[i] == 255)
|
||||
break;
|
||||
|
||||
rct_string_id itemStringId = window_new_campaign_shop_items[i] + 2016;
|
||||
if (itemStringId >= 2048)
|
||||
itemStringId += 96;
|
||||
|
||||
gDropdownItemsFormat[i] = 1142;
|
||||
gDropdownItemsArgs[i] = itemStringId;
|
||||
numItems++;
|
||||
}
|
||||
|
||||
window_dropdown_show_text_custom_width(
|
||||
w->x + dropdownWidget->left,
|
||||
w->y + dropdownWidget->top,
|
||||
dropdownWidget->bottom - dropdownWidget->top + 1,
|
||||
w->colours[1],
|
||||
0x80,
|
||||
numItems,
|
||||
dropdownWidget->right - dropdownWidget->left - 3
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (window_new_campaign_rides[0] != 255) {
|
||||
int numItems = 0;
|
||||
for (int i = 0; i < 40; i++) {
|
||||
if (window_new_campaign_rides[i] == 255)
|
||||
break;
|
||||
|
||||
rct_ride *ride = GET_RIDE(window_new_campaign_rides[i]);
|
||||
gDropdownItemsFormat[i] = 1142;
|
||||
gDropdownItemsArgs[i] = (ride->var_04C << 16) | ride->var_04A;
|
||||
numItems++;
|
||||
}
|
||||
|
||||
window_dropdown_show_text_custom_width(
|
||||
w->x + dropdownWidget->left,
|
||||
w->y + dropdownWidget->top,
|
||||
dropdownWidget->bottom - dropdownWidget->top + 1,
|
||||
w->colours[1],
|
||||
0x80,
|
||||
numItems,
|
||||
dropdownWidget->right - dropdownWidget->left - 3
|
||||
);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case WIDX_WEEKS_INCREASE_BUTTON:
|
||||
w->var_482 = min(w->var_482 + 1, 6);
|
||||
window_invalidate(w);
|
||||
break;
|
||||
case WIDX_WEEKS_DECREASE_BUTTON:
|
||||
w->var_482 = max(w->var_482 - 1, 2);
|
||||
window_invalidate(w);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* rct2: 0x0069E537
|
||||
*/
|
||||
static void window_new_campaign_dropdown()
|
||||
{
|
||||
rct_window *w;
|
||||
short widgetIndex, dropdownIndex;
|
||||
|
||||
window_dropdown_get_registers(w, widgetIndex, dropdownIndex);
|
||||
|
||||
if (widgetIndex != WIDX_RIDE_DROPDOWN_BUTTON)
|
||||
return;
|
||||
|
||||
if (w->var_480 == ADVERTISING_CAMPAIGN_FOOD_OR_DRINK_FREE) {
|
||||
rct_string_id itemStringId = (uint16)gDropdownItemsArgs[dropdownIndex] - 2016;
|
||||
if (itemStringId >= 32)
|
||||
itemStringId -= 96;
|
||||
w->var_484 = itemStringId;
|
||||
} else {
|
||||
w->var_484 = window_new_campaign_rides[dropdownIndex];
|
||||
}
|
||||
|
||||
window_invalidate(w);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* rct2: 0x0069E397
|
||||
*/
|
||||
static void window_new_campaign_invalidate()
|
||||
{
|
||||
rct_window *w;
|
||||
|
||||
window_get_register(w);
|
||||
|
||||
window_new_campaign_widgets[WIDX_RIDE_LABEL].type = WWT_EMPTY;
|
||||
window_new_campaign_widgets[WIDX_RIDE_DROPDOWN].type = WWT_EMPTY;
|
||||
window_new_campaign_widgets[WIDX_RIDE_DROPDOWN_BUTTON].type = WWT_EMPTY;
|
||||
window_new_campaign_widgets[WIDX_RIDE_DROPDOWN].image = STR_MARKETING_NOT_SELECTED;
|
||||
switch (w->var_480) {
|
||||
case ADVERTISING_CAMPAIGN_RIDE_FREE:
|
||||
case ADVERTISING_CAMPAIGN_RIDE:
|
||||
window_new_campaign_widgets[WIDX_RIDE_LABEL].type = WWT_24;
|
||||
window_new_campaign_widgets[WIDX_RIDE_DROPDOWN].type = WWT_DROPDOWN;
|
||||
window_new_campaign_widgets[WIDX_RIDE_DROPDOWN_BUTTON].type = WWT_DROPDOWN_BUTTON;
|
||||
window_new_campaign_widgets[WIDX_RIDE_LABEL].image = STR_MARKETING_RIDE;
|
||||
if (w->var_484 != SELECTED_RIDE_UNDEFINED) {
|
||||
rct_ride *ride = GET_RIDE(w->var_484);
|
||||
window_new_campaign_widgets[WIDX_RIDE_DROPDOWN].image = ride->var_04A;
|
||||
RCT2_GLOBAL(0x013CE952, uint32) = ride->var_04C;
|
||||
}
|
||||
break;
|
||||
case ADVERTISING_CAMPAIGN_FOOD_OR_DRINK_FREE:
|
||||
window_new_campaign_widgets[WIDX_RIDE_LABEL].type = WWT_24;
|
||||
window_new_campaign_widgets[WIDX_RIDE_DROPDOWN].type = WWT_DROPDOWN;
|
||||
window_new_campaign_widgets[WIDX_RIDE_DROPDOWN_BUTTON].type = WWT_DROPDOWN_BUTTON;
|
||||
window_new_campaign_widgets[WIDX_RIDE_LABEL].image = STR_MARKETING_ITEM;
|
||||
if (w->var_484 != SELECTED_RIDE_UNDEFINED) {
|
||||
rct_string_id itemStringId = w->var_484 + 2016;
|
||||
if (itemStringId >= 2048)
|
||||
itemStringId += 96;
|
||||
window_new_campaign_widgets[WIDX_RIDE_DROPDOWN].image = itemStringId;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Set current number of weeks spinner
|
||||
window_new_campaign_widgets[WIDX_WEEKS_SPINNER].image = (STR_MARKETING_1_WEEK - 1) + w->var_482;
|
||||
|
||||
// Enable / disable start button based on ride dropdown
|
||||
w->disabled_widgets &= ~(1 << WIDX_START_BUTTON);
|
||||
if (window_new_campaign_widgets[WIDX_RIDE_DROPDOWN].type == WWT_DROPDOWN && w->var_484 == SELECTED_RIDE_UNDEFINED)
|
||||
w->disabled_widgets |= 1 << WIDX_START_BUTTON;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* rct2: 0x0069E493
|
||||
*/
|
||||
static void window_new_campaign_paint()
|
||||
{
|
||||
rct_window *w;
|
||||
rct_drawpixelinfo *dpi;
|
||||
int x, y;
|
||||
|
||||
window_paint_get_registers(w, dpi);
|
||||
|
||||
window_draw_widgets(w, dpi);
|
||||
|
||||
x = w->x + 14;
|
||||
y = w->y + 60;
|
||||
|
||||
// Price per week
|
||||
money32 pricePerWeek = AdvertisingCampaignPricePerWeek[w->var_480];
|
||||
gfx_draw_string_left(dpi, STR_MARKETING_COST_PER_WEEK, &pricePerWeek, 0, x, y);
|
||||
y += 13;
|
||||
|
||||
// Total price
|
||||
money32 totalPrice = AdvertisingCampaignPricePerWeek[w->var_480] * w->var_482;
|
||||
gfx_draw_string_left(dpi, STR_MARKETING_TOTAL_COST, &totalPrice, 0, x, y);
|
||||
}
|
||||
Reference in New Issue
Block a user