From c849a75bc823c9073c3da71d7d522e182bed0ae2 Mon Sep 17 00:00:00 2001 From: Tulio Leao Date: Thu, 27 Apr 2023 07:05:14 -0300 Subject: [PATCH] Close #13782: Refactor Multiplayer window to class (#19948) --- src/openrct2-ui/windows/Multiplayer.cpp | 1094 +++++++++++------------ 1 file changed, 508 insertions(+), 586 deletions(-) diff --git a/src/openrct2-ui/windows/Multiplayer.cpp b/src/openrct2-ui/windows/Multiplayer.cpp index b4eda4b377..b13670afab 100644 --- a/src/openrct2-ui/windows/Multiplayer.cpp +++ b/src/openrct2-ui/windows/Multiplayer.cpp @@ -118,96 +118,6 @@ static constexpr StringId WindowMultiplayerPageTitles[] = { STR_MULTIPLAYER_OPTIONS_TITLE, }; -static uint8_t _selectedGroup = 0; - -static void WindowMultiplayerInformationMouseup(WindowBase *w, WidgetIndex widgetIndex); -static void WindowMultiplayerInformationResize(WindowBase *w); -static void WindowMultiplayerInformationUpdate(WindowBase *w); -static void WindowMultiplayerInformationInvalidate(WindowBase *w); -static void WindowMultiplayerInformationPaint(WindowBase *w, DrawPixelInfo& dpi); - -static void WindowMultiplayerPlayersMouseup(WindowBase *w, WidgetIndex widgetIndex); -static void WindowMultiplayerPlayersResize(WindowBase *w); -static void WindowMultiplayerPlayersUpdate(WindowBase *w); -static void WindowMultiplayerPlayersScrollgetsize(WindowBase *w, int32_t scrollIndex, int32_t *width, int32_t *height); -static void WindowMultiplayerPlayersScrollmousedown(WindowBase *w, int32_t scrollIndex, const ScreenCoordsXY& screenCoords); -static void WindowMultiplayerPlayersScrollmouseover(WindowBase *w, int32_t scrollIndex, const ScreenCoordsXY& screenCoords); -static void WindowMultiplayerPlayersInvalidate(WindowBase *w); -static void WindowMultiplayerPlayersPaint(WindowBase *w, DrawPixelInfo& dpi); -static void WindowMultiplayerPlayersScrollpaint(WindowBase *w, DrawPixelInfo& dpi, int32_t scrollIndex); - -static void WindowMultiplayerGroupsMouseup(WindowBase *w, WidgetIndex widgetIndex); -static void WindowMultiplayerGroupsResize(WindowBase *w); -static void WindowMultiplayerGroupsMousedown(WindowBase *w, WidgetIndex widgetIndex, Widget* widget); -static void WindowMultiplayerGroupsDropdown(WindowBase *w, WidgetIndex widgetIndex, int32_t dropdownIndex); -static void WindowMultiplayerGroupsUpdate(WindowBase *w); -static void WindowMultiplayerGroupsScrollgetsize(WindowBase *w, int32_t scrollIndex, int32_t *width, int32_t *height); -static void WindowMultiplayerGroupsScrollmousedown(WindowBase *w, int32_t scrollIndex, const ScreenCoordsXY& screenCoords); -static void WindowMultiplayerGroupsScrollmouseover(WindowBase *w, int32_t scrollIndex, const ScreenCoordsXY& screenCoords); -static void WindowMultiplayerGroupsTextInput(WindowBase *w, WidgetIndex widgetIndex, const char *text); -static void WindowMultiplayerGroupsInvalidate(WindowBase *w); -static void WindowMultiplayerGroupsPaint(WindowBase *w, DrawPixelInfo& dpi); -static void WindowMultiplayerGroupsScrollpaint(WindowBase *w, DrawPixelInfo& dpi, int32_t scrollIndex); - -static void WindowMultiplayerOptionsMouseup(WindowBase *w, WidgetIndex widgetIndex); -static void WindowMultiplayerOptionsResize(WindowBase *w); -static void WindowMultiplayerOptionsUpdate(WindowBase *w); -static void WindowMultiplayerOptionsInvalidate(WindowBase *w); -static void WindowMultiplayerOptionsPaint(WindowBase *w, DrawPixelInfo& dpi); - -static WindowEventList window_multiplayer_information_events([](auto& events) -{ - events.mouse_up = &WindowMultiplayerInformationMouseup; - events.resize = &WindowMultiplayerInformationResize; - events.update = &WindowMultiplayerInformationUpdate; - events.invalidate = &WindowMultiplayerInformationInvalidate; - events.paint = &WindowMultiplayerInformationPaint; -}); - -static WindowEventList window_multiplayer_players_events([](auto& events) -{ - events.mouse_up = &WindowMultiplayerPlayersMouseup; - events.resize = &WindowMultiplayerPlayersResize; - events.update = &WindowMultiplayerPlayersUpdate; - events.get_scroll_size = &WindowMultiplayerPlayersScrollgetsize; - events.scroll_mousedown = &WindowMultiplayerPlayersScrollmousedown; - events.scroll_mouseover = &WindowMultiplayerPlayersScrollmouseover; - events.invalidate = &WindowMultiplayerPlayersInvalidate; - events.paint = &WindowMultiplayerPlayersPaint; - events.scroll_paint = &WindowMultiplayerPlayersScrollpaint; -}); - -static WindowEventList window_multiplayer_groups_events([](auto& events) -{ - events.mouse_up = &WindowMultiplayerGroupsMouseup; - events.resize = &WindowMultiplayerGroupsResize; - events.mouse_down = &WindowMultiplayerGroupsMousedown; - events.dropdown = &WindowMultiplayerGroupsDropdown; - events.update = &WindowMultiplayerGroupsUpdate; - events.get_scroll_size = &WindowMultiplayerGroupsScrollgetsize; - events.scroll_mousedown = &WindowMultiplayerGroupsScrollmousedown; - events.scroll_mouseover = &WindowMultiplayerGroupsScrollmouseover; - events.text_input = &WindowMultiplayerGroupsTextInput; - events.invalidate = &WindowMultiplayerGroupsInvalidate; - events.paint = &WindowMultiplayerGroupsPaint; - events.scroll_paint = &WindowMultiplayerGroupsScrollpaint; -}); - -static WindowEventList window_multiplayer_options_events([](auto& events) -{ - events.mouse_up = &WindowMultiplayerOptionsMouseup; - events.resize = &WindowMultiplayerOptionsResize; - events.update = &WindowMultiplayerOptionsUpdate; - events.invalidate = &WindowMultiplayerOptionsInvalidate; - events.paint = &WindowMultiplayerOptionsPaint; -}); - -static WindowEventList *window_multiplayer_page_events[] = { - &window_multiplayer_information_events, - &window_multiplayer_players_events, - &window_multiplayer_groups_events, - &window_multiplayer_options_events, -}; // clang-format on static constexpr const int32_t window_multiplayer_animation_divisor[] = { @@ -223,11 +133,53 @@ static constexpr const int32_t window_multiplayer_animation_frames[] = { 4, }; -static void WindowMultiplayerDrawTabImages(WindowBase* w, DrawPixelInfo& dpi); -static void WindowMultiplayerSetPage(WindowBase* w, int32_t page); +static bool IsServerPlayerInvisible() +{ + return NetworkIsServerPlayerInvisible() && !gConfigGeneral.DebuggingTools; +} -static bool _windowInformationSizeDirty; -static ScreenCoordsXY _windowInformationSize; +class MultiplayerWindow final : public Window +{ +private: + std::optional _windowInformationSize; + uint8_t _selectedGroup{ 0 }; + +private: + void ResetPressedWidgets(); + + void InformationPaint(DrawPixelInfo& dpi); + void PlayersPaint(DrawPixelInfo& dpi); + void GroupsPaint(DrawPixelInfo& dpi); + + void DrawTabImage(DrawPixelInfo& dpi, int32_t page_number, int32_t spriteIndex); + void DrawTabImages(DrawPixelInfo& dpi); + + void PlayersScrollPaint(int32_t scrollIndex, DrawPixelInfo& dpi) const; + void GroupsScrollPaint(int32_t scrollIndex, DrawPixelInfo& dpi) const; + + void ShowGroupDropdown(WidgetIndex widgetIndex); + ScreenCoordsXY InformationGetSize(); + +public: + MultiplayerWindow(); + + void SetPage(int32_t page_number); + + void OnMouseUp(WidgetIndex widgetIndex) override; + void OnResize() override; + void OnUpdate() override; + void OnPrepareDraw() override; + void OnDraw(DrawPixelInfo& dpi) override; + + void OnDropdown(WidgetIndex widgetIndex, int32_t selectedIndex) override; + void OnTextInput(WidgetIndex widgetIndex, std::string_view text) override; + void OnMouseDown(WidgetIndex widgetIndex) override; + + ScreenSize OnScrollGetSize(int32_t scrollIndex) override; + void OnScrollMouseDown(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override; + void OnScrollMouseOver(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override; + void OnScrollDraw(int32_t scrollIndex, DrawPixelInfo& dpi) override; +}; WindowBase* WindowMultiplayerOpen() { @@ -235,63 +187,342 @@ WindowBase* WindowMultiplayerOpen() WindowBase* window = WindowBringToFrontByClass(WindowClass::Multiplayer); if (window == nullptr) { - window = WindowCreateAutoPos( - 320, 144, &window_multiplayer_players_events, WindowClass::Multiplayer, WF_10 | WF_RESIZABLE); - WindowMultiplayerSetPage(window, WINDOW_MULTIPLAYER_PAGE_INFORMATION); + window = WindowCreate(WindowClass::Multiplayer, 320, 144, WF_10 | WF_RESIZABLE | WF_AUTO_POSITION); } return window; } -static void WindowMultiplayerSetPage(WindowBase* w, int32_t page) +MultiplayerWindow::MultiplayerWindow() { - _windowInformationSizeDirty = true; - - w->page = page; - w->frame_no = 0; - w->no_list_items = 0; - w->selected_list_item = -1; - - w->hold_down_widgets = 0; - w->event_handlers = window_multiplayer_page_events[page]; - w->pressed_widgets = 0; - w->widgets = window_multiplayer_page_widgets[page]; - w->widgets[WIDX_TITLE].text = WindowMultiplayerPageTitles[page]; - - WindowEventResizeCall(w); - WindowEventInvalidateCall(w); - WindowInitScrollWidgets(*w); - w->Invalidate(); + SetPage(WINDOW_MULTIPLAYER_PAGE_INFORMATION); } -static void WindowMultiplayerAnchorBorderWidgets(WindowBase* w) +void MultiplayerWindow::SetPage(int32_t page_number) { - w->ResizeFrameWithPage(); + _windowInformationSize.reset(); + + page = page_number; + frame_no = 0; + no_list_items = 0; + selected_list_item = -1; + + hold_down_widgets = 0; + pressed_widgets = 0; + widgets = window_multiplayer_page_widgets[page]; + widgets[WIDX_TITLE].text = WindowMultiplayerPageTitles[page]; + + WindowEventResizeCall(this); + WindowEventInvalidateCall(this); + InitScrollWidgets(); + Invalidate(); } -static void WindowMultiplayerSetPressedTab(WindowBase* w) +void MultiplayerWindow::OnMouseUp(WidgetIndex widgetIndex) +{ + switch (widgetIndex) + { + case WIDX_CLOSE: + Close(); + break; + case WIDX_TAB1: + case WIDX_TAB2: + case WIDX_TAB3: + case WIDX_TAB4: + if (page != widgetIndex - WIDX_TAB1) + { + SetPage(widgetIndex - WIDX_TAB1); + } + break; + } + switch (page) + { + case WINDOW_MULTIPLAYER_PAGE_GROUPS: + { + switch (widgetIndex) + { + case WIDX_ADD_GROUP: + { + auto networkModifyGroup = NetworkModifyGroupAction(ModifyGroupType::AddGroup); + GameActions::Execute(&networkModifyGroup); + break; + } + case WIDX_REMOVE_GROUP: + { + auto networkModifyGroup = NetworkModifyGroupAction(ModifyGroupType::RemoveGroup, _selectedGroup); + GameActions::Execute(&networkModifyGroup); + break; + } + case WIDX_RENAME_GROUP: + { + int32_t groupIndex = NetworkGetGroupIndex(_selectedGroup); + const utf8* groupName = NetworkGetGroupName(groupIndex); + WindowTextInputRawOpen( + this, widgetIndex, STR_GROUP_NAME, STR_ENTER_NEW_NAME_FOR_THIS_GROUP, {}, groupName, 32); + break; + } + } + break; + } + case WINDOW_MULTIPLAYER_PAGE_OPTIONS: + { + switch (widgetIndex) + { + case WIDX_LOG_CHAT_CHECKBOX: + gConfigNetwork.LogChat = !gConfigNetwork.LogChat; + ConfigSaveDefault(); + break; + case WIDX_LOG_SERVER_ACTIONS_CHECKBOX: + gConfigNetwork.LogServerActions = !gConfigNetwork.LogServerActions; + ConfigSaveDefault(); + break; + case WIDX_KNOWN_KEYS_ONLY_CHECKBOX: + gConfigNetwork.KnownKeysOnly = !gConfigNetwork.KnownKeysOnly; + ConfigSaveDefault(); + break; + } + break; + } + } +} + +ScreenCoordsXY MultiplayerWindow::InformationGetSize() +{ + assert(!_windowInformationSize.has_value()); + + int32_t lineHeight = FontGetLineHeight(FontStyle::Medium); + + // Base dimensions. + const int32_t baseWidth = 450; + int32_t baseHeight = 55; + + // Server name is displayed word-wrapped, so figure out how high it will be. + { + int32_t numLines; + GfxWrapString(NetworkGetServerName(), baseWidth, FontStyle::Medium, nullptr, &numLines); + baseHeight += (numLines + 1) * lineHeight + (LIST_ROW_HEIGHT / 2); + } + + // Likewise, for the optional server description -- which can be a little longer. + const auto& descString = NetworkGetServerDescription(); + if (!descString.empty()) + { + int32_t numLines; + GfxWrapString(descString, baseWidth, FontStyle::Medium, nullptr, &numLines); + baseHeight += (numLines + 1) * lineHeight + (LIST_ROW_HEIGHT / 2); + } + + // Finally, account for provider info, if present. + { + const auto& providerName = NetworkGetServerProviderName(); + if (!providerName.empty()) + baseHeight += LIST_ROW_HEIGHT; + + const auto& providerEmail = NetworkGetServerProviderEmail(); + if (!providerEmail.empty()) + baseHeight += LIST_ROW_HEIGHT; + + const auto& providerWebsite = NetworkGetServerProviderWebsite(); + if (!providerWebsite.empty()) + baseHeight += LIST_ROW_HEIGHT; + } + + // TODO: Are these casts still neccessary? + _windowInformationSize = { static_cast(baseWidth), static_cast(baseHeight) }; + return _windowInformationSize.value(); +} + +void MultiplayerWindow::OnResize() +{ + switch (page) + { + case WINDOW_MULTIPLAYER_PAGE_INFORMATION: + { + auto size = _windowInformationSize ? _windowInformationSize.value() : InformationGetSize(); + WindowSetResize(*this, size.x, size.y, size.x, size.y); + break; + } + case WINDOW_MULTIPLAYER_PAGE_PLAYERS: + { + WindowSetResize(*this, 420, 124, 500, 450); + + no_list_items = (IsServerPlayerInvisible() ? NetworkGetNumVisiblePlayers() : NetworkGetNumPlayers()); + list_item_positions[0] = 0; + + widgets[WIDX_HEADER_PING].right = width - 5; + + selected_list_item = -1; + Invalidate(); + break; + } + case WINDOW_MULTIPLAYER_PAGE_GROUPS: + { + WindowSetResize(*this, 320, 200, 320, 500); + + no_list_items = NetworkGetNumActions(); + list_item_positions[0] = 0; + + selected_list_item = -1; + Invalidate(); + break; + } + case WINDOW_MULTIPLAYER_PAGE_OPTIONS: + { + WindowSetResize(*this, 300, 100, 300, 100); + break; + } + } +} + +void MultiplayerWindow::OnUpdate() +{ + frame_no++; + InvalidateWidget(WIDX_TAB1 + page); +} + +void MultiplayerWindow::ResetPressedWidgets() { for (int32_t i = WIDX_TAB1; i <= WIDX_TAB4; i++) { - w->pressed_widgets &= ~(1 << i); + SetWidgetPressed(i, false); } - w->pressed_widgets |= 1LL << (WIDX_TAB1 + w->page); } -static void WindowMultiplayerGroupsShowGroupDropdown(WindowBase* w, Widget* widget) +void MultiplayerWindow::OnPrepareDraw() { - Widget* dropdownWidget; - int32_t numItems, i; + ResetPressedWidgets(); + SetWidgetPressed(WIDX_TAB1 + page, true); + ResizeFrameWithPage(); + switch (page) + { + case WINDOW_MULTIPLAYER_PAGE_INFORMATION: + { + WindowAlignTabs(this, WIDX_TAB1, WIDX_TAB4); + break; + } + case WINDOW_MULTIPLAYER_PAGE_PLAYERS: + { + window_multiplayer_players_widgets[WIDX_LIST].right = width - 4; + window_multiplayer_players_widgets[WIDX_LIST].bottom = height - 0x0F; + WindowAlignTabs(this, WIDX_TAB1, WIDX_TAB4); + break; + } + case WINDOW_MULTIPLAYER_PAGE_GROUPS: + { + window_multiplayer_groups_widgets[WIDX_PERMISSIONS_LIST].right = width - 4; + window_multiplayer_groups_widgets[WIDX_PERMISSIONS_LIST].bottom = height - 0x0F; + WindowAlignTabs(this, WIDX_TAB1, WIDX_TAB4); - dropdownWidget = widget - 1; + // select other group if one is removed + while (NetworkGetGroupIndex(_selectedGroup) == -1 && _selectedGroup > 0) + { + _selectedGroup--; + } + break; + } + case WINDOW_MULTIPLAYER_PAGE_OPTIONS: + { + WindowAlignTabs(this, WIDX_TAB1, WIDX_TAB4); - numItems = NetworkGetNumGroups(); + if (NetworkGetMode() == NETWORK_MODE_CLIENT) + { + widgets[WIDX_KNOWN_KEYS_ONLY_CHECKBOX].type = WindowWidgetType::Empty; + } + + SetCheckboxValue(WIDX_LOG_CHAT_CHECKBOX, gConfigNetwork.LogChat); + SetCheckboxValue(WIDX_LOG_SERVER_ACTIONS_CHECKBOX, gConfigNetwork.LogServerActions); + SetCheckboxValue(WIDX_KNOWN_KEYS_ONLY_CHECKBOX, gConfigNetwork.KnownKeysOnly); + break; + } + } +} + +void MultiplayerWindow::OnDraw(DrawPixelInfo& dpi) +{ + DrawWidgets(dpi); + DrawTabImages(dpi); + switch (page) + { + case WINDOW_MULTIPLAYER_PAGE_INFORMATION: + { + InformationPaint(dpi); + break; + } + case WINDOW_MULTIPLAYER_PAGE_PLAYERS: + { + PlayersPaint(dpi); + break; + } + case WINDOW_MULTIPLAYER_PAGE_GROUPS: + { + GroupsPaint(dpi); + break; + } + } +} + +void MultiplayerWindow::OnDropdown(WidgetIndex widgetIndex, int32_t selectedIndex) +{ + switch (page) + { + case WINDOW_MULTIPLAYER_PAGE_GROUPS: + { + if (selectedIndex == -1) + { + return; + } + + switch (widgetIndex) + { + case WIDX_DEFAULT_GROUP_DROPDOWN: + { + auto networkModifyGroup = NetworkModifyGroupAction( + ModifyGroupType::SetDefault, NetworkGetGroupID(selectedIndex)); + GameActions::Execute(&networkModifyGroup); + break; + } + case WIDX_SELECTED_GROUP_DROPDOWN: + { + _selectedGroup = NetworkGetGroupID(selectedIndex); + break; + } + } + Invalidate(); + break; + } + } +} + +void MultiplayerWindow::OnTextInput(WidgetIndex widgetIndex, std::string_view text) +{ + switch (page) + { + case WINDOW_MULTIPLAYER_PAGE_GROUPS: + { + if (widgetIndex != WIDX_RENAME_GROUP) + return; + + if (text.empty()) + return; + + auto networkModifyGroup = NetworkModifyGroupAction(ModifyGroupType::SetName, _selectedGroup, std::string(text)); + GameActions::Execute(&networkModifyGroup); + break; + } + } +} + +void MultiplayerWindow::ShowGroupDropdown(WidgetIndex widgetIndex) +{ + auto widget = &widgets[widgetIndex]; + Widget* dropdownWidget = widget - 1; + auto numItems = NetworkGetNumGroups(); WindowDropdownShowTextCustomWidth( - { w->windowPos.x + dropdownWidget->left, w->windowPos.y + dropdownWidget->top }, dropdownWidget->height() + 1, - w->colours[1], 0, 0, numItems, widget->right - dropdownWidget->left); + windowPos + ScreenCoordsXY{ dropdownWidget->left, dropdownWidget->top }, dropdownWidget->height() + 1, colours[1], 0, 0, + numItems, widget->right - dropdownWidget->left); - for (i = 0; i < NetworkGetNumGroups(); i++) + for (auto i = 0; i < NetworkGetNumGroups(); i++) { gDropdownItems[i].Format = STR_OPTIONS_DROPDOWN_ITEM; gDropdownItems[i].Args = reinterpret_cast(NetworkGetGroupName(i)); @@ -306,111 +537,154 @@ static void WindowMultiplayerGroupsShowGroupDropdown(WindowBase* w, Widget* widg } } -#pragma region Information page - -static void WindowMultiplayerInformationMouseup(WindowBase* w, WidgetIndex widgetIndex) +void MultiplayerWindow::OnMouseDown(WidgetIndex widgetIndex) { - switch (widgetIndex) + switch (page) { - case WIDX_CLOSE: - WindowClose(*w); - break; - case WIDX_TAB1: - case WIDX_TAB2: - case WIDX_TAB3: - case WIDX_TAB4: - if (w->page != widgetIndex - WIDX_TAB1) + case WINDOW_MULTIPLAYER_PAGE_GROUPS: + { + switch (widgetIndex) { - WindowMultiplayerSetPage(w, widgetIndex - WIDX_TAB1); + case WIDX_DEFAULT_GROUP_DROPDOWN: + case WIDX_SELECTED_GROUP_DROPDOWN: + ShowGroupDropdown(widgetIndex); + break; } break; + } } } -static ScreenCoordsXY WindowMultiplayerInformationGetSize() +ScreenSize MultiplayerWindow::OnScrollGetSize(int32_t scrollIndex) { - if (!_windowInformationSizeDirty) + ScreenSize screenSize{}; + switch (page) { - return _windowInformationSize; + case WINDOW_MULTIPLAYER_PAGE_PLAYERS: + { + if (selected_list_item != -1) + { + selected_list_item = -1; + Invalidate(); + } + + screenSize = { 0, NetworkGetNumPlayers() * SCROLLABLE_ROW_HEIGHT }; + int32_t i = screenSize.height - window_multiplayer_players_widgets[WIDX_LIST].bottom + + window_multiplayer_players_widgets[WIDX_LIST].top + 21; + if (i < 0) + i = 0; + if (i < scrolls[0].v_top) + { + scrolls[0].v_top = i; + Invalidate(); + } + break; + } + + case WINDOW_MULTIPLAYER_PAGE_GROUPS: + { + if (selected_list_item != -1) + { + selected_list_item = -1; + Invalidate(); + } + + screenSize = { 0, NetworkGetNumActions() * SCROLLABLE_ROW_HEIGHT }; + int32_t i = screenSize.height - window_multiplayer_groups_widgets[WIDX_LIST].bottom + + window_multiplayer_groups_widgets[WIDX_LIST].top + 21; + if (i < 0) + i = 0; + if (i < scrolls[0].v_top) + { + scrolls[0].v_top = i; + Invalidate(); + } + break; + } } + return screenSize; +} - int32_t lineHeight = FontGetLineHeight(FontStyle::Medium); - - // Base dimensions. - const int32_t width = 450; - int32_t height = 55; - - // Server name is displayed word-wrapped, so figure out how high it will be. +void MultiplayerWindow::OnScrollMouseDown(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) +{ + switch (page) { - int32_t numLines; - GfxWrapString(NetworkGetServerName(), width, FontStyle::Medium, nullptr, &numLines); - height += (numLines + 1) * lineHeight + (LIST_ROW_HEIGHT / 2); - } + case WINDOW_MULTIPLAYER_PAGE_PLAYERS: + { + int32_t index = screenCoords.y / SCROLLABLE_ROW_HEIGHT; + if (index >= no_list_items) + return; - // Likewise, for the optional server description -- which can be a little longer. - const auto& descString = NetworkGetServerDescription(); - if (!descString.empty()) + selected_list_item = index; + Invalidate(); + + int32_t player = (IsServerPlayerInvisible() ? index + 1 : index); + WindowPlayerOpen(NetworkGetPlayerID(player)); + break; + } + + case WINDOW_MULTIPLAYER_PAGE_GROUPS: + { + int32_t index = screenCoords.y / SCROLLABLE_ROW_HEIGHT; + if (index >= no_list_items) + return; + + selected_list_item = index; + Invalidate(); + + auto networkModifyGroup = NetworkModifyGroupAction( + ModifyGroupType::SetPermissions, _selectedGroup, "", index, PermissionState::Toggle); + GameActions::Execute(&networkModifyGroup); + break; + } + } +} + +void MultiplayerWindow::OnScrollMouseOver(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) +{ + switch (page) { - int32_t numLines; - GfxWrapString(descString, width, FontStyle::Medium, nullptr, &numLines); - height += (numLines + 1) * lineHeight + (LIST_ROW_HEIGHT / 2); - } + case WINDOW_MULTIPLAYER_PAGE_PLAYERS: + case WINDOW_MULTIPLAYER_PAGE_GROUPS: + { + int32_t index = screenCoords.y / SCROLLABLE_ROW_HEIGHT; + if (index >= no_list_items) + return; - // Finally, account for provider info, if present. + selected_list_item = index; + Invalidate(); + break; + } + } +} + +void MultiplayerWindow::OnScrollDraw(int32_t scrollIndex, DrawPixelInfo& dpi) +{ + switch (page) { - const auto& providerName = NetworkGetServerProviderName(); - if (!providerName.empty()) - height += LIST_ROW_HEIGHT; + case WINDOW_MULTIPLAYER_PAGE_PLAYERS: + PlayersScrollPaint(scrollIndex, dpi); + break; - const auto& providerEmail = NetworkGetServerProviderEmail(); - if (!providerEmail.empty()) - height += LIST_ROW_HEIGHT; - - const auto& providerWebsite = NetworkGetServerProviderWebsite(); - if (!providerWebsite.empty()) - height += LIST_ROW_HEIGHT; + case WINDOW_MULTIPLAYER_PAGE_GROUPS: + GroupsScrollPaint(scrollIndex, dpi); + break; } - - _windowInformationSizeDirty = false; - _windowInformationSize = { static_cast(width), static_cast(height) }; - return _windowInformationSize; } -static void WindowMultiplayerInformationResize(WindowBase* w) +void MultiplayerWindow::InformationPaint(DrawPixelInfo& dpi) { - auto size = WindowMultiplayerInformationGetSize(); - WindowSetResize(*w, size.x, size.y, size.x, size.y); -} - -static void WindowMultiplayerInformationUpdate(WindowBase* w) -{ - w->frame_no++; - WidgetInvalidate(*w, WIDX_TAB1 + w->page); -} - -static void WindowMultiplayerInformationInvalidate(WindowBase* w) -{ - WindowMultiplayerSetPressedTab(w); - WindowMultiplayerAnchorBorderWidgets(w); - WindowAlignTabs(w, WIDX_TAB1, WIDX_TAB4); -} - -static void WindowMultiplayerInformationPaint(WindowBase* w, DrawPixelInfo& dpi) -{ - WindowDrawWidgets(*w, dpi); - WindowMultiplayerDrawTabImages(w, dpi); - DrawPixelInfo clippedDPI; - if (ClipDrawPixelInfo(clippedDPI, dpi, w->windowPos, w->width, w->height)) + if (ClipDrawPixelInfo(clippedDPI, dpi, windowPos, width, height)) { auto screenCoords = ScreenCoordsXY{ 3, 50 }; - int32_t width = w->width - 6; + int32_t newWidth = width - 6; const auto& name = NetworkGetServerName(); { auto ft = Formatter(); ft.Add(name.c_str()); - screenCoords.y += DrawTextWrapped(clippedDPI, screenCoords, width, STR_STRING, ft, { w->colours[1] }); + screenCoords.y += DrawTextWrapped(clippedDPI, screenCoords, newWidth, STR_STRING, ft, { colours[1] }); screenCoords.y += LIST_ROW_HEIGHT / 2; } @@ -419,7 +693,7 @@ static void WindowMultiplayerInformationPaint(WindowBase* w, DrawPixelInfo& dpi) { auto ft = Formatter(); ft.Add(description.c_str()); - screenCoords.y += DrawTextWrapped(clippedDPI, screenCoords, width, STR_STRING, ft, { w->colours[1] }); + screenCoords.y += DrawTextWrapped(clippedDPI, screenCoords, newWidth, STR_STRING, ft, { colours[1] }); screenCoords.y += LIST_ROW_HEIGHT / 2; } @@ -451,126 +725,17 @@ static void WindowMultiplayerInformationPaint(WindowBase* w, DrawPixelInfo& dpi) } } -#pragma endregion - -#pragma region Players page - -static bool IsServerPlayerInvisible() +void MultiplayerWindow::PlayersPaint(DrawPixelInfo& dpi) { - return NetworkIsServerPlayerInvisible() && !gConfigGeneral.DebuggingTools; -} - -static void WindowMultiplayerPlayersMouseup(WindowBase* w, WidgetIndex widgetIndex) -{ - switch (widgetIndex) - { - case WIDX_CLOSE: - WindowClose(*w); - break; - case WIDX_TAB1: - case WIDX_TAB2: - case WIDX_TAB3: - case WIDX_TAB4: - if (w->page != widgetIndex - WIDX_TAB1) - { - WindowMultiplayerSetPage(w, widgetIndex - WIDX_TAB1); - } - break; - } -} - -static void WindowMultiplayerPlayersResize(WindowBase* w) -{ - WindowSetResize(*w, 420, 124, 500, 450); - - w->no_list_items = (IsServerPlayerInvisible() ? NetworkGetNumVisiblePlayers() : NetworkGetNumPlayers()); - w->list_item_positions[0] = 0; - - w->widgets[WIDX_HEADER_PING].right = w->width - 5; - - w->selected_list_item = -1; - w->Invalidate(); -} - -static void WindowMultiplayerPlayersUpdate(WindowBase* w) -{ - w->frame_no++; - WidgetInvalidate(*w, WIDX_TAB1 + w->page); -} - -static void WindowMultiplayerPlayersScrollgetsize(WindowBase* w, int32_t scrollIndex, int32_t* width, int32_t* height) -{ - int32_t i; - - if (w->selected_list_item != -1) - { - w->selected_list_item = -1; - w->Invalidate(); - } - - *height = NetworkGetNumPlayers() * SCROLLABLE_ROW_HEIGHT; - i = *height - window_multiplayer_players_widgets[WIDX_LIST].bottom + window_multiplayer_players_widgets[WIDX_LIST].top + 21; - if (i < 0) - i = 0; - if (i < w->scrolls[0].v_top) - { - w->scrolls[0].v_top = i; - w->Invalidate(); - } -} - -static void WindowMultiplayerPlayersScrollmousedown(WindowBase* w, int32_t scrollIndex, const ScreenCoordsXY& screenCoords) -{ - int32_t index; - - index = screenCoords.y / SCROLLABLE_ROW_HEIGHT; - if (index >= w->no_list_items) - return; - - w->selected_list_item = index; - w->Invalidate(); - - int32_t player = (IsServerPlayerInvisible() ? index + 1 : index); - WindowPlayerOpen(NetworkGetPlayerID(player)); -} - -static void WindowMultiplayerPlayersScrollmouseover(WindowBase* w, int32_t scrollIndex, const ScreenCoordsXY& screenCoords) -{ - int32_t index; - - index = screenCoords.y / SCROLLABLE_ROW_HEIGHT; - if (index >= w->no_list_items) - return; - - w->selected_list_item = index; - w->Invalidate(); -} - -static void WindowMultiplayerPlayersInvalidate(WindowBase* w) -{ - WindowMultiplayerSetPressedTab(w); - WindowMultiplayerAnchorBorderWidgets(w); - window_multiplayer_players_widgets[WIDX_LIST].right = w->width - 4; - window_multiplayer_players_widgets[WIDX_LIST].bottom = w->height - 0x0F; - WindowAlignTabs(w, WIDX_TAB1, WIDX_TAB4); -} - -static void WindowMultiplayerPlayersPaint(WindowBase* w, DrawPixelInfo& dpi) -{ - StringId stringId; - - WindowDrawWidgets(*w, dpi); - WindowMultiplayerDrawTabImages(w, dpi); - // Number of players - stringId = w->no_list_items == 1 ? STR_MULTIPLAYER_PLAYER_COUNT : STR_MULTIPLAYER_PLAYER_COUNT_PLURAL; - auto screenCoords = w->windowPos + ScreenCoordsXY{ 4, w->widgets[WIDX_LIST].bottom + 2 }; + StringId stringId = no_list_items == 1 ? STR_MULTIPLAYER_PLAYER_COUNT : STR_MULTIPLAYER_PLAYER_COUNT_PLURAL; + auto screenCoords = windowPos + ScreenCoordsXY{ 4, widgets[WIDX_LIST].bottom + 2 }; auto ft = Formatter(); - ft.Add(w->no_list_items); - DrawTextBasic(dpi, screenCoords, stringId, ft, { w->colours[2] }); + ft.Add(no_list_items); + DrawTextBasic(dpi, screenCoords, stringId, ft, { colours[2] }); } -static void WindowMultiplayerPlayersScrollpaint(WindowBase* w, DrawPixelInfo& dpi, int32_t scrollIndex) +void MultiplayerWindow::PlayersScrollPaint(int32_t scrollIndex, DrawPixelInfo& dpi) const { ScreenCoordsXY screenCoords; screenCoords.y = 0; @@ -593,13 +758,13 @@ static void WindowMultiplayerPlayersScrollpaint(WindowBase* w, DrawPixelInfo& dp // Draw player name colour_t colour = COLOUR_BLACK; - if (listPosition == w->selected_list_item) + if (listPosition == selected_list_item) { GfxFilterRect( dpi, { 0, screenCoords.y, 800, screenCoords.y + SCROLLABLE_ROW_HEIGHT - 1 }, FilterPaletteID::PaletteDarken1); _buffer += NetworkGetPlayerName(player); - colour = w->colours[2]; + colour = colours[2]; } else { @@ -670,182 +835,10 @@ static void WindowMultiplayerPlayersScrollpaint(WindowBase* w, DrawPixelInfo& dp } } -#pragma endregion - -#pragma region Groups page - -static void WindowMultiplayerGroupsMouseup(WindowBase* w, WidgetIndex widgetIndex) -{ - switch (widgetIndex) - { - case WIDX_CLOSE: - WindowClose(*w); - break; - case WIDX_TAB1: - case WIDX_TAB2: - case WIDX_TAB3: - case WIDX_TAB4: - if (w->page != widgetIndex - WIDX_TAB1) - { - WindowMultiplayerSetPage(w, widgetIndex - WIDX_TAB1); - } - break; - case WIDX_ADD_GROUP: - { - auto networkModifyGroup = NetworkModifyGroupAction(ModifyGroupType::AddGroup); - GameActions::Execute(&networkModifyGroup); - } - break; - case WIDX_REMOVE_GROUP: - { - auto networkModifyGroup = NetworkModifyGroupAction(ModifyGroupType::RemoveGroup, _selectedGroup); - GameActions::Execute(&networkModifyGroup); - } - break; - case WIDX_RENAME_GROUP:; - int32_t groupIndex = NetworkGetGroupIndex(_selectedGroup); - const utf8* groupName = NetworkGetGroupName(groupIndex); - WindowTextInputRawOpen(w, widgetIndex, STR_GROUP_NAME, STR_ENTER_NEW_NAME_FOR_THIS_GROUP, {}, groupName, 32); - break; - } -} - -static void WindowMultiplayerGroupsResize(WindowBase* w) -{ - WindowSetResize(*w, 320, 200, 320, 500); - - w->no_list_items = NetworkGetNumActions(); - w->list_item_positions[0] = 0; - - w->selected_list_item = -1; - w->Invalidate(); -} - -static void WindowMultiplayerGroupsMousedown(WindowBase* w, WidgetIndex widgetIndex, Widget* widget) -{ - switch (widgetIndex) - { - case WIDX_DEFAULT_GROUP_DROPDOWN: - WindowMultiplayerGroupsShowGroupDropdown(w, widget); - break; - case WIDX_SELECTED_GROUP_DROPDOWN: - WindowMultiplayerGroupsShowGroupDropdown(w, widget); - break; - } -} - -static void WindowMultiplayerGroupsDropdown(WindowBase* w, WidgetIndex widgetIndex, int32_t dropdownIndex) -{ - if (dropdownIndex == -1) - { - return; - } - - switch (widgetIndex) - { - case WIDX_DEFAULT_GROUP_DROPDOWN: - { - auto networkModifyGroup = NetworkModifyGroupAction(ModifyGroupType::SetDefault, NetworkGetGroupID(dropdownIndex)); - GameActions::Execute(&networkModifyGroup); - } - break; - case WIDX_SELECTED_GROUP_DROPDOWN: - _selectedGroup = NetworkGetGroupID(dropdownIndex); - break; - } - - w->Invalidate(); -} - -static void WindowMultiplayerGroupsUpdate(WindowBase* w) -{ - w->frame_no++; - WidgetInvalidate(*w, WIDX_TAB1 + w->page); -} - -static void WindowMultiplayerGroupsScrollgetsize(WindowBase* w, int32_t scrollIndex, int32_t* width, int32_t* height) -{ - int32_t i; - - if (w->selected_list_item != -1) - { - w->selected_list_item = -1; - w->Invalidate(); - } - - *height = NetworkGetNumActions() * SCROLLABLE_ROW_HEIGHT; - i = *height - window_multiplayer_groups_widgets[WIDX_LIST].bottom + window_multiplayer_groups_widgets[WIDX_LIST].top + 21; - if (i < 0) - i = 0; - if (i < w->scrolls[0].v_top) - { - w->scrolls[0].v_top = i; - w->Invalidate(); - } -} - -static void WindowMultiplayerGroupsScrollmousedown(WindowBase* w, int32_t scrollIndex, const ScreenCoordsXY& screenCoords) -{ - int32_t index; - - index = screenCoords.y / SCROLLABLE_ROW_HEIGHT; - if (index >= w->no_list_items) - return; - - w->selected_list_item = index; - w->Invalidate(); - - auto networkModifyGroup = NetworkModifyGroupAction( - ModifyGroupType::SetPermissions, _selectedGroup, "", index, PermissionState::Toggle); - GameActions::Execute(&networkModifyGroup); -} - -static void WindowMultiplayerGroupsScrollmouseover(WindowBase* w, int32_t scrollIndex, const ScreenCoordsXY& screenCoords) -{ - int32_t index; - - index = screenCoords.y / SCROLLABLE_ROW_HEIGHT; - if (index >= w->no_list_items) - return; - - w->selected_list_item = index; - w->Invalidate(); -} - -static void WindowMultiplayerGroupsTextInput(WindowBase* w, WidgetIndex widgetIndex, const char* text) -{ - if (widgetIndex != WIDX_RENAME_GROUP) - return; - - if (text == nullptr) - return; - - auto networkModifyGroup = NetworkModifyGroupAction(ModifyGroupType::SetName, _selectedGroup, text); - GameActions::Execute(&networkModifyGroup); -} - -static void WindowMultiplayerGroupsInvalidate(WindowBase* w) -{ - WindowMultiplayerSetPressedTab(w); - WindowMultiplayerAnchorBorderWidgets(w); - window_multiplayer_groups_widgets[WIDX_PERMISSIONS_LIST].right = w->width - 4; - window_multiplayer_groups_widgets[WIDX_PERMISSIONS_LIST].bottom = w->height - 0x0F; - WindowAlignTabs(w, WIDX_TAB1, WIDX_TAB4); - - // select other group if one is removed - while (NetworkGetGroupIndex(_selectedGroup) == -1 && _selectedGroup > 0) - { - _selectedGroup--; - } -} - -static void WindowMultiplayerGroupsPaint(WindowBase* w, DrawPixelInfo& dpi) +void MultiplayerWindow::GroupsPaint(DrawPixelInfo& dpi) { thread_local std::string _buffer; - WindowDrawWidgets(*w, dpi); - WindowMultiplayerDrawTabImages(w, dpi); - Widget* widget = &window_multiplayer_groups_widgets[WIDX_DEFAULT_GROUP]; int32_t group = NetworkGetGroupIndex(NetworkGetDefaultGroup()); if (group != -1) @@ -856,20 +849,20 @@ static void WindowMultiplayerGroupsPaint(WindowBase* w, DrawPixelInfo& dpi) auto ft = Formatter(); ft.Add(_buffer.c_str()); DrawTextEllipsised( - dpi, w->windowPos + ScreenCoordsXY{ widget->midX() - 5, widget->top }, widget->width() - 8, STR_STRING, ft, + dpi, windowPos + ScreenCoordsXY{ widget->midX() - 5, widget->top }, widget->width() - 8, STR_STRING, ft, { TextAlignment::CENTRE }); } - auto screenPos = w->windowPos + auto screenPos = windowPos + ScreenCoordsXY{ window_multiplayer_groups_widgets[WIDX_CONTENT_PANEL].left + 4, window_multiplayer_groups_widgets[WIDX_CONTENT_PANEL].top + 4 }; - DrawTextBasic(dpi, screenPos, STR_DEFAULT_GROUP, {}, { w->colours[2] }); + DrawTextBasic(dpi, screenPos, STR_DEFAULT_GROUP, {}, { colours[2] }); screenPos.y += 20; GfxFillRectInset( - dpi, { screenPos - ScreenCoordsXY{ 0, 6 }, screenPos + ScreenCoordsXY{ 310, -5 } }, w->colours[1], + dpi, { screenPos - ScreenCoordsXY{ 0, 6 }, screenPos + ScreenCoordsXY{ 310, -5 } }, colours[1], INSET_RECT_FLAG_BORDER_INSET); widget = &window_multiplayer_groups_widgets[WIDX_SELECTED_GROUP]; @@ -881,22 +874,22 @@ static void WindowMultiplayerGroupsPaint(WindowBase* w, DrawPixelInfo& dpi) auto ft = Formatter(); ft.Add(_buffer.c_str()); DrawTextEllipsised( - dpi, w->windowPos + ScreenCoordsXY{ widget->midX() - 5, widget->top }, widget->width() - 8, STR_STRING, ft, + dpi, windowPos + ScreenCoordsXY{ widget->midX() - 5, widget->top }, widget->width() - 8, STR_STRING, ft, { TextAlignment::CENTRE }); } } -static void WindowMultiplayerGroupsScrollpaint(WindowBase* w, DrawPixelInfo& dpi, int32_t scrollIndex) +void MultiplayerWindow::GroupsScrollPaint(int32_t scrollIndex, DrawPixelInfo& dpi) const { auto screenCoords = ScreenCoordsXY{ 0, 0 }; auto dpiCoords = ScreenCoordsXY{ dpi.x, dpi.y }; GfxFillRect( - dpi, { dpiCoords, dpiCoords + ScreenCoordsXY{ dpi.width - 1, dpi.height - 1 } }, ColourMapA[w->colours[1]].mid_light); + dpi, { dpiCoords, dpiCoords + ScreenCoordsXY{ dpi.width - 1, dpi.height - 1 } }, ColourMapA[colours[1]].mid_light); for (int32_t i = 0; i < NetworkGetNumActions(); i++) { - if (i == w->selected_list_item) + if (i == selected_list_item) { GfxFilterRect( dpi, { 0, screenCoords.y, 800, screenCoords.y + SCROLLABLE_ROW_HEIGHT - 1 }, FilterPaletteID::PaletteDarken1); @@ -927,102 +920,31 @@ static void WindowMultiplayerGroupsScrollpaint(WindowBase* w, DrawPixelInfo& dpi } } -#pragma endregion - -#pragma region Options page - -static void WindowMultiplayerOptionsMouseup(WindowBase* w, WidgetIndex widgetIndex) +void MultiplayerWindow::DrawTabImage(DrawPixelInfo& dpi, int32_t page_number, int32_t spriteIndex) { - switch (widgetIndex) + WidgetIndex widgetIndex = WIDX_TAB1 + page_number; + + if (!IsWidgetDisabled(widgetIndex)) { - case WIDX_CLOSE: - WindowClose(*w); - break; - case WIDX_TAB1: - case WIDX_TAB2: - case WIDX_TAB3: - case WIDX_TAB4: - if (w->page != widgetIndex - WIDX_TAB1) - { - WindowMultiplayerSetPage(w, widgetIndex - WIDX_TAB1); - } - break; - case WIDX_LOG_CHAT_CHECKBOX: - gConfigNetwork.LogChat = !gConfigNetwork.LogChat; - ConfigSaveDefault(); - break; - case WIDX_LOG_SERVER_ACTIONS_CHECKBOX: - gConfigNetwork.LogServerActions = !gConfigNetwork.LogServerActions; - ConfigSaveDefault(); - break; - case WIDX_KNOWN_KEYS_ONLY_CHECKBOX: - gConfigNetwork.KnownKeysOnly = !gConfigNetwork.KnownKeysOnly; - ConfigSaveDefault(); - break; - } -} - -static void WindowMultiplayerOptionsResize(WindowBase* w) -{ - WindowSetResize(*w, 300, 100, 300, 100); -} - -static void WindowMultiplayerOptionsUpdate(WindowBase* w) -{ - w->frame_no++; - WidgetInvalidate(*w, WIDX_TAB1 + w->page); -} - -static void WindowMultiplayerOptionsInvalidate(WindowBase* w) -{ - WindowMultiplayerSetPressedTab(w); - WindowMultiplayerAnchorBorderWidgets(w); - WindowAlignTabs(w, WIDX_TAB1, WIDX_TAB4); - - if (NetworkGetMode() == NETWORK_MODE_CLIENT) - { - w->widgets[WIDX_KNOWN_KEYS_ONLY_CHECKBOX].type = WindowWidgetType::Empty; - } - - WidgetSetCheckboxValue(*w, WIDX_LOG_CHAT_CHECKBOX, gConfigNetwork.LogChat); - WidgetSetCheckboxValue(*w, WIDX_LOG_SERVER_ACTIONS_CHECKBOX, gConfigNetwork.LogServerActions); - WidgetSetCheckboxValue(*w, WIDX_KNOWN_KEYS_ONLY_CHECKBOX, gConfigNetwork.KnownKeysOnly); -} - -static void WindowMultiplayerOptionsPaint(WindowBase* w, DrawPixelInfo& dpi) -{ - WindowDrawWidgets(*w, dpi); - WindowMultiplayerDrawTabImages(w, dpi); -} - -#pragma endregion - -static void WindowMultiplayerDrawTabImage(WindowBase* w, DrawPixelInfo& dpi, int32_t page, int32_t spriteIndex) -{ - WidgetIndex widgetIndex = WIDX_TAB1 + page; - - if (!WidgetIsDisabled(*w, widgetIndex)) - { - if (w->page == page) + if (page == page_number) { - int32_t numFrames = window_multiplayer_animation_frames[w->page]; + int32_t numFrames = window_multiplayer_animation_frames[page]; if (numFrames > 1) { - int32_t frame = w->frame_no / window_multiplayer_animation_divisor[w->page]; + int32_t frame = frame_no / window_multiplayer_animation_divisor[page]; spriteIndex += (frame % numFrames); } } GfxDrawSprite( - dpi, ImageId(spriteIndex), - w->windowPos + ScreenCoordsXY{ w->widgets[widgetIndex].left, w->widgets[widgetIndex].top }); + dpi, ImageId(spriteIndex), windowPos + ScreenCoordsXY{ widgets[widgetIndex].left, widgets[widgetIndex].top }); } } -static void WindowMultiplayerDrawTabImages(WindowBase* w, DrawPixelInfo& dpi) +void MultiplayerWindow::DrawTabImages(DrawPixelInfo& dpi) { - WindowMultiplayerDrawTabImage(w, dpi, WINDOW_MULTIPLAYER_PAGE_INFORMATION, SPR_TAB_KIOSKS_AND_FACILITIES_0); - WindowMultiplayerDrawTabImage(w, dpi, WINDOW_MULTIPLAYER_PAGE_PLAYERS, SPR_TAB_GUESTS_0); - WindowMultiplayerDrawTabImage(w, dpi, WINDOW_MULTIPLAYER_PAGE_GROUPS, SPR_TAB_STAFF_OPTIONS_0); - WindowMultiplayerDrawTabImage(w, dpi, WINDOW_MULTIPLAYER_PAGE_OPTIONS, SPR_TAB_GEARS_0); + DrawTabImage(dpi, WINDOW_MULTIPLAYER_PAGE_INFORMATION, SPR_TAB_KIOSKS_AND_FACILITIES_0); + DrawTabImage(dpi, WINDOW_MULTIPLAYER_PAGE_PLAYERS, SPR_TAB_GUESTS_0); + DrawTabImage(dpi, WINDOW_MULTIPLAYER_PAGE_GROUPS, SPR_TAB_STAFF_OPTIONS_0); + DrawTabImage(dpi, WINDOW_MULTIPLAYER_PAGE_OPTIONS, SPR_TAB_GEARS_0); }