diff options
author | Patric Stout <truebrain@openttd.org> | 2021-04-18 09:54:47 +0200 |
---|---|---|
committer | Patric Stout <github@truebrain.nl> | 2021-04-24 21:43:58 +0200 |
commit | 526635942451479bee66e9eb61c50f91ae48a7dd (patch) | |
tree | 34a757634102f384eb2e70ae1044dd86916243f9 | |
parent | aca20092aadd65a764f1ea493c36f0ca507b32ff (diff) | |
download | openttd-526635942451479bee66e9eb61c50f91ae48a7dd.tar.xz |
Feature: rework in-game Online Players GUI
The GUI now more clearly shows some basic information about the
server you joined, your client name (and the ability to change it),
and what players are in which company.
It also contains useful buttons to press to join companies, chat
with other people, and for admins to kick/ban people.
Additionally, renamed "advertised" to "visibility"; this has to
do with future additions, but also because it is more clear in
wording.
-rw-r--r-- | media/baseset/openttd.grf | bin | 510358 -> 510525 bytes | |||
-rw-r--r-- | media/baseset/openttd/openttdgui.nfo | 5 | ||||
-rw-r--r-- | media/baseset/openttd/openttdgui.png | bin | 43899 -> 44464 bytes | |||
-rw-r--r-- | src/company_cmd.cpp | 4 | ||||
-rw-r--r-- | src/lang/english.txt | 41 | ||||
-rw-r--r-- | src/network/network.cpp | 5 | ||||
-rw-r--r-- | src/network/network_client.cpp | 10 | ||||
-rw-r--r-- | src/network/network_gui.cpp | 625 | ||||
-rw-r--r-- | src/network/network_server.cpp | 7 | ||||
-rw-r--r-- | src/table/sprites.h | 6 | ||||
-rw-r--r-- | src/toolbar_gui.cpp | 14 | ||||
-rw-r--r-- | src/widgets/network_widget.h | 11 |
12 files changed, 563 insertions, 165 deletions
diff --git a/media/baseset/openttd.grf b/media/baseset/openttd.grf Binary files differindex a63bfc4e2..c4511f55e 100644 --- a/media/baseset/openttd.grf +++ b/media/baseset/openttd.grf diff --git a/media/baseset/openttd/openttdgui.nfo b/media/baseset/openttd/openttdgui.nfo index eb1313365..9a13fa800 100644 --- a/media/baseset/openttd/openttdgui.nfo +++ b/media/baseset/openttd/openttdgui.nfo @@ -4,7 +4,7 @@ // See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>. // -1 * 0 0C "OpenTTD GUI graphics" - -1 * 3 05 15 \b 186 // OPENTTD_SPRITE_COUNT + -1 * 3 05 15 \b 189 // OPENTTD_SPRITE_COUNT -1 sprites/openttdgui.png 8bpp 66 8 64 31 -31 7 normal -1 sprites/openttdgui.png 8bpp 146 8 64 31 -31 7 normal -1 sprites/openttdgui.png 8bpp 226 8 64 31 -31 7 normal @@ -191,3 +191,6 @@ -1 sprites/openttdgui_convert_tram.png 8bpp 24 0 32 32 0 0 normal -1 sprites/openttdgui.png 8bpp 513 440 10 10 0 0 normal -1 sprites/openttdgui.png 8bpp 526 440 10 10 0 0 normal + -1 sprites/openttdgui.png 8bpp 539 440 12 10 0 0 normal + -1 sprites/openttdgui.png 8bpp 553 440 12 10 0 0 normal + -1 sprites/openttdgui.png 8bpp 567 440 12 10 0 0 normal diff --git a/media/baseset/openttd/openttdgui.png b/media/baseset/openttd/openttdgui.png Binary files differindex 1fc02aa61..dc0976a97 100644 --- a/media/baseset/openttd/openttdgui.png +++ b/media/baseset/openttd/openttdgui.png diff --git a/src/company_cmd.cpp b/src/company_cmd.cpp index ab9e7e12e..c3483a9bf 100644 --- a/src/company_cmd.cpp +++ b/src/company_cmd.cpp @@ -571,8 +571,8 @@ Company *DoStartupNewCompany(bool is_ai, CompanyID company = INVALID_COMPANY) GeneratePresidentName(c); SetWindowDirty(WC_GRAPH_LEGEND, 0); - SetWindowClassesDirty(WC_CLIENT_LIST_POPUP); - SetWindowDirty(WC_CLIENT_LIST, 0); + InvalidateWindowClassesData(WC_CLIENT_LIST_POPUP); + InvalidateWindowData(WC_CLIENT_LIST, 0); InvalidateWindowData(WC_LINKGRAPH_LEGEND, 0); BuildOwnerLegend(); InvalidateWindowData(WC_SMALLMAP, 0, 1); diff --git a/src/lang/english.txt b/src/lang/english.txt index ff12f122a..13cde55d9 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -1991,6 +1991,9 @@ STR_FACE_TIE :Tie: STR_FACE_EARRING :Earring: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Change tie or earring +STR_NETWORK_SERVER_VISIBILITY_PRIVATE :Private +STR_NETWORK_SERVER_VISIBILITY_PUBLIC :Public + # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Multiplayer STR_NETWORK_SERVER_LIST_PLAYER_NAME :{BLACK}Player name: @@ -2053,10 +2056,8 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}The game STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Set password STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Protect your game with a password if you don't want it to be publicly accessible -STR_NETWORK_START_SERVER_ADVERTISED_LABEL :{BLACK}Advertised -STR_NETWORK_START_SERVER_ADVERTISED_TOOLTIP :{BLACK}Choose between an advertised (internet) and a not advertised (Local Area Network, LAN) game -STR_NETWORK_START_SERVER_UNADVERTISED :No -STR_NETWORK_START_SERVER_ADVERTISED :Yes +STR_NETWORK_START_SERVER_VISIBILITY_LABEL :{BLACK}Visibility +STR_NETWORK_START_SERVER_VISIBILITY_TOOLTIP :{BLACK}Whether other people can see your server in the public listing STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} client{P "" s} STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Maximum number of clients: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Choose the maximum number of clients. Not all slots need to be filled @@ -2118,19 +2119,37 @@ STR_NETWORK_CONNECTION_DISCONNECT :{BLACK}Disconne STR_NETWORK_NEED_GAME_PASSWORD_CAPTION :{WHITE}Server is protected. Enter password STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Company is protected. Enter password -STR_NETWORK_COMPANY_LIST_CLIENT_LIST_CAPTION :{WHITE}Client list # Network company list added strings -STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Client list -STR_NETWORK_COMPANY_LIST_SPECTATE :Spectate +STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Online players STR_NETWORK_COMPANY_LIST_NEW_COMPANY :New company -# Network client list +# Network client list popup for clients STR_NETWORK_CLIENTLIST_KICK :Kick STR_NETWORK_CLIENTLIST_BAN :Ban -STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL :Speak to all -STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY :Speak to company -STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT :Private message + +# Network client list +STR_NETWORK_CLIENT_LIST_CAPTION :{WHITE}Multiplayer +STR_NETWORK_CLIENT_LIST_SERVER :{BLACK}Server +STR_NETWORK_CLIENT_LIST_SERVER_NAME :{BLACK}Name +STR_NETWORK_CLIENT_LIST_SERVER_NAME_TOOLTIP :{BLACK}Name of the server you are playing on +STR_NETWORK_CLIENT_LIST_SERVER_NAME_EDIT_TOOLTIP :{BLACK}Edit the name of your server +STR_NETWORK_CLIENT_LIST_SERVER_NAME_QUERY_CAPTION :Name of the server +STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY :{BLACK}Visibility +STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY_TOOLTIP :{BLACK}Whether other people can see your server in the public listing +STR_NETWORK_CLIENT_LIST_PLAYER :{BLACK}Player +STR_NETWORK_CLIENT_LIST_PLAYER_NAME :{BLACK}Name +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_TOOLTIP :{BLACK}Your player name +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_EDIT_TOOLTIP :{BLACK}Edit your player name +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_QUERY_CAPTION :Your player name +STR_NETWORK_CLIENT_LIST_PLAYER_HOST :{WHITE}(host) {BLACK}{RAW_STRING} +STR_NETWORK_CLIENT_LIST_PLAYER_SELF :{WHITE}(you) {BLACK}{RAW_STRING} +STR_NETWORK_CLIENT_LIST_ADMIN_TOOLTIP :{BLACK}Administrative actions to perform for this client +STR_NETWORK_CLIENT_LIST_JOIN_TOOLTIP :{BLACK}Join this company +STR_NETWORK_CLIENT_LIST_CHAT_CLIENT_TOOLTIP :{BLACK}Send a message to this player +STR_NETWORK_CLIENT_LIST_CHAT_COMPANY_TOOLTIP :{BLACK}Send a message to all players of this company +STR_NETWORK_CLIENT_LIST_CHAT_SPECTATOR_TOOLTIP :{BLACK}Send a message to all spectators +STR_NETWORK_CLIENT_LIST_SPECTATORS :Spectators STR_NETWORK_SERVER :Server STR_NETWORK_CLIENT :Client diff --git a/src/network/network.cpp b/src/network/network.cpp index 97eadb976..1e0838684 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -522,9 +522,10 @@ void ParseGameConnectionString(const char **company, const char **port, char *co /* Register the login */ _network_clients_connected++; - SetWindowDirty(WC_CLIENT_LIST, 0); ServerNetworkGameSocketHandler *cs = new ServerNetworkGameSocketHandler(s); cs->client_address = address; // Save the IP of the client + + InvalidateWindowData(WC_CLIENT_LIST, 0); } /** @@ -713,7 +714,7 @@ void NetworkClientConnectGame(NetworkAddress address, CompanyID join_as, const c static void NetworkInitGameInfo() { if (StrEmpty(_settings_client.network.server_name)) { - seprintf(_settings_client.network.server_name, lastof(_settings_client.network.server_name), "Unnamed Server"); + strecpy(_settings_client.network.server_name, "Unnamed Server", lastof(_settings_client.network.server_name)); } /* The server is a client too */ diff --git a/src/network/network_client.cpp b/src/network/network_client.cpp index 6156dc486..d5fe64b11 100644 --- a/src/network/network_client.cpp +++ b/src/network/network_client.cpp @@ -647,7 +647,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_CLIENT_INFO(Pac ci->client_playas = playas; strecpy(ci->client_name, name, lastof(ci->client_name)); - SetWindowDirty(WC_CLIENT_LIST, 0); + InvalidateWindowData(WC_CLIENT_LIST, 0); return NETWORK_RECV_STATUS_OKAY; } @@ -666,7 +666,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_CLIENT_INFO(Pac strecpy(ci->client_name, name, lastof(ci->client_name)); - SetWindowDirty(WC_CLIENT_LIST, 0); + InvalidateWindowData(WC_CLIENT_LIST, 0); return NETWORK_RECV_STATUS_OKAY; } @@ -1043,7 +1043,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_ERROR_QUIT(Pack delete ci; } - SetWindowDirty(WC_CLIENT_LIST, 0); + InvalidateWindowData(WC_CLIENT_LIST, 0); return NETWORK_RECV_STATUS_OKAY; } @@ -1062,7 +1062,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_QUIT(Packet *p) DEBUG(net, 0, "Unknown client (%d) is leaving the game", client_id); } - SetWindowDirty(WC_CLIENT_LIST, 0); + InvalidateWindowData(WC_CLIENT_LIST, 0); /* If we come here it means we could not locate the client.. strange :s */ return NETWORK_RECV_STATUS_OKAY; @@ -1079,7 +1079,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_JOIN(Packet *p) NetworkTextMessage(NETWORK_ACTION_JOIN, CC_DEFAULT, false, ci->client_name); } - SetWindowDirty(WC_CLIENT_LIST, 0); + InvalidateWindowData(WC_CLIENT_LIST, 0); return NETWORK_RECV_STATUS_OKAY; } diff --git a/src/network/network_gui.cpp b/src/network/network_gui.cpp index dd25ad5bc..fcfeded5a 100644 --- a/src/network/network_gui.cpp +++ b/src/network/network_gui.cpp @@ -21,6 +21,7 @@ #include "network_udp.h" #include "../window_func.h" #include "../gfx_func.h" +#include "../widgets/dropdown_type.h" #include "../widgets/dropdown_func.h" #include "../querystring_gui.h" #include "../sortlist_type.h" @@ -30,6 +31,8 @@ #include "../map_type.h" #include "../guitimer_func.h" #include "../zoom_func.h" +#include "../sprite.h" +#include "../settings_internal.h" #include "../widgets/network_widget.h" @@ -38,21 +41,24 @@ #include "../stringfilter_type.h" -#include "../safeguards.h" - #ifdef __EMSCRIPTEN__ # include <emscripten.h> #endif +#include <map> + +#include "../safeguards.h" + static void ShowNetworkStartServerWindow(); static void ShowNetworkLobbyWindow(NetworkGameList *ngl); /** - * Advertisement options in the start server window + * Visibility of the server. Public servers advertise, where private servers + * do not. */ -static const StringID _connection_types_dropdown[] = { - STR_NETWORK_START_SERVER_UNADVERTISED, - STR_NETWORK_START_SERVER_ADVERTISED, +static const StringID _server_visibility_dropdown[] = { + STR_NETWORK_SERVER_VISIBILITY_PRIVATE, + STR_NETWORK_SERVER_VISIBILITY_PUBLIC, INVALID_STRING_ID }; @@ -985,7 +991,7 @@ struct NetworkStartServerWindow : public Window { { switch (widget) { case WID_NSS_CONNTYPE_BTN: - SetDParam(0, _connection_types_dropdown[_settings_client.network.server_advertise]); + SetDParam(0, _server_visibility_dropdown[_settings_client.network.server_advertise]); break; case WID_NSS_CLIENTS_TXT: @@ -1006,7 +1012,7 @@ struct NetworkStartServerWindow : public Window { { switch (widget) { case WID_NSS_CONNTYPE_BTN: - *size = maxdim(GetStringBoundingBox(_connection_types_dropdown[0]), GetStringBoundingBox(_connection_types_dropdown[1])); + *size = maxdim(GetStringBoundingBox(_server_visibility_dropdown[0]), GetStringBoundingBox(_server_visibility_dropdown[1])); size->width += padding.width; size->height += padding.height; break; @@ -1036,7 +1042,7 @@ struct NetworkStartServerWindow : public Window { break; case WID_NSS_CONNTYPE_BTN: // Connection type - ShowDropDownMenu(this, _connection_types_dropdown, _settings_client.network.server_advertise, WID_NSS_CONNTYPE_BTN, 0, 0); // do it for widget WID_NSS_CONNTYPE_BTN + ShowDropDownMenu(this, _server_visibility_dropdown, _settings_client.network.server_advertise, WID_NSS_CONNTYPE_BTN, 0, 0); // do it for widget WID_NSS_CONNTYPE_BTN break; case WID_NSS_CLIENTS_BTND: case WID_NSS_CLIENTS_BTNU: // Click on up/down button for number of clients @@ -1175,8 +1181,8 @@ static const NWidgetPart _nested_network_start_server_window_widgets[] = { NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), SetPIP(10, 6, 10), NWidget(NWID_VERTICAL), SetPIP(0, 1, 0), - NWidget(WWT_TEXT, COLOUR_LIGHT_BLUE, WID_NSS_CONNTYPE_LABEL), SetFill(1, 0), SetDataTip(STR_NETWORK_START_SERVER_ADVERTISED_LABEL, STR_NULL), - NWidget(WWT_DROPDOWN, COLOUR_LIGHT_BLUE, WID_NSS_CONNTYPE_BTN), SetFill(1, 0), SetDataTip(STR_BLACK_STRING, STR_NETWORK_START_SERVER_ADVERTISED_TOOLTIP), + NWidget(WWT_TEXT, COLOUR_LIGHT_BLUE, WID_NSS_CONNTYPE_LABEL), SetFill(1, 0), SetDataTip(STR_NETWORK_START_SERVER_VISIBILITY_LABEL, STR_NULL), + NWidget(WWT_DROPDOWN, COLOUR_LIGHT_BLUE, WID_NSS_CONNTYPE_BTN), SetFill(1, 0), SetDataTip(STR_BLACK_STRING, STR_NETWORK_START_SERVER_VISIBILITY_TOOLTIP), EndContainer(), NWidget(NWID_VERTICAL), SetPIP(0, 1, 0), NWidget(NWID_SPACER), SetFill(1, 1), @@ -1592,21 +1598,6 @@ static void ClientList_Ban(const NetworkClientInfo *ci) NetworkServerKickOrBanIP(ci->client_id, true, nullptr); } -static void ClientList_SpeakToClient(const NetworkClientInfo *ci) -{ - ShowNetworkChatQueryWindow(DESTTYPE_CLIENT, ci->client_id); -} - -static void ClientList_SpeakToCompany(const NetworkClientInfo *ci) -{ - ShowNetworkChatQueryWindow(DESTTYPE_TEAM, ci->client_playas); -} - -static void ClientList_SpeakToAll(const NetworkClientInfo *ci) -{ - ShowNetworkChatQueryWindow(DESTTYPE_BROADCAST, 0); -} - /** Popup selection window to chose an action to perform */ struct NetworkClientListPopupWindow : Window { /** Container for actions that can be executed. */ @@ -1639,15 +1630,6 @@ struct NetworkClientListPopupWindow : Window { const NetworkClientInfo *ci = NetworkClientInfo::GetByClientID(client_id); - if (_network_own_client_id != ci->client_id) { - this->AddAction(STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT, &ClientList_SpeakToClient); - } - - if (Company::IsValidID(ci->client_playas) || ci->client_playas == COMPANY_SPECTATOR) { - this->AddAction(STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY, &ClientList_SpeakToCompany); - } - this->AddAction(STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL, &ClientList_SpeakToAll); - /* A server can kick clients (but not himself). */ if (_network_server && _network_own_client_id != ci->client_id) { this->AddAction(STR_NETWORK_CLIENTLIST_KICK, &ClientList_Kick); @@ -1671,7 +1653,7 @@ struct NetworkClientListPopupWindow : Window { } d.height *= (uint)this->actions.size(); - d.width += WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT; + d.width += WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT + 4 + 4; // Give the list a bit of padding on both sides. d.height += WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM; *size = d; } @@ -1690,7 +1672,7 @@ struct NetworkClientListPopupWindow : Window { colour = TC_BLACK; } - DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, action.name, colour); + DrawString(r.left + WD_FRAMERECT_LEFT + 4, r.right - WD_FRAMERECT_RIGHT - 4, y, action.name, colour); y += FONT_HEIGHT_NORMAL; } } @@ -1731,166 +1713,553 @@ static void PopupClientList(ClientID client_id, int x, int y) static const NWidgetPart _nested_client_list_widgets[] = { NWidget(NWID_HORIZONTAL), NWidget(WWT_CLOSEBOX, COLOUR_GREY), - NWidget(WWT_CAPTION, COLOUR_GREY), SetDataTip(STR_NETWORK_COMPANY_LIST_CLIENT_LIST_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), + NWidget(WWT_CAPTION, COLOUR_GREY), SetDataTip(STR_NETWORK_CLIENT_LIST_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), + NWidget(WWT_DEFSIZEBOX, COLOUR_GREY), NWidget(WWT_STICKYBOX, COLOUR_GREY), EndContainer(), - NWidget(WWT_PANEL, COLOUR_GREY, WID_CL_PANEL), SetMinimalSize(250, WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM), SetResize(1, 1), EndContainer(), + NWidget(WWT_PANEL, COLOUR_GREY), + NWidget(NWID_SELECTION, INVALID_COLOUR, WID_CL_SERVER_SELECTOR), + NWidget(WWT_FRAME, COLOUR_GREY), SetDataTip(STR_NETWORK_CLIENT_LIST_SERVER, STR_NULL), SetPadding(4, 4, 0, 4), SetPIP(0, 2, 0), + NWidget(NWID_HORIZONTAL), SetPIP(0, 3, 0), + NWidget(WWT_TEXT, COLOUR_GREY), SetMinimalTextLines(1, 0), SetDataTip(STR_NETWORK_CLIENT_LIST_SERVER_NAME, STR_NULL), + NWidget(NWID_SPACER), SetMinimalSize(20, 0), + NWidget(WWT_TEXT, COLOUR_GREY, WID_CL_SERVER_NAME), SetFill(1, 0), SetMinimalTextLines(1, 0), SetResize(1, 0), SetDataTip(STR_BLACK_RAW_STRING, STR_NETWORK_CLIENT_LIST_SERVER_NAME_TOOLTIP), SetAlignment(SA_VERT_CENTER | SA_RIGHT), + NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_CL_SERVER_NAME_EDIT), SetMinimalSize(12, 14), SetDataTip(SPR_RENAME, STR_NETWORK_CLIENT_LIST_SERVER_NAME_EDIT_TOOLTIP), + EndContainer(), + NWidget(NWID_HORIZONTAL), SetPIP(0, 3, 0), + NWidget(WWT_TEXT, COLOUR_GREY), SetMinimalTextLines(1, 0), SetDataTip(STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY, STR_NULL), + NWidget(NWID_SPACER), SetMinimalSize(20, 0), SetFill(1, 0), SetResize(1, 0), + NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_CL_SERVER_VISIBILITY), SetDataTip(STR_BLACK_STRING, STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY_TOOLTIP), + EndContainer(), + EndContainer(), + EndContainer(), + NWidget(WWT_FRAME, COLOUR_GREY), SetDataTip(STR_NETWORK_CLIENT_LIST_PLAYER, STR_NULL), SetPadding(4, 4, 4, 4), SetPIP(0, 2, 0), + NWidget(NWID_HORIZONTAL), SetPIP(0, 3, 0), + NWidget(WWT_TEXT, COLOUR_GREY), SetMinimalTextLines(1, 0), SetDataTip(STR_NETWORK_CLIENT_LIST_PLAYER_NAME, STR_NULL), + NWidget(NWID_SPACER), SetMinimalSize(20, 0), + NWidget(WWT_TEXT, COLOUR_GREY, WID_CL_CLIENT_NAME), SetFill(1, 0), SetMinimalTextLines(1, 0), SetResize(1, 0), SetDataTip(STR_BLACK_RAW_STRING, STR_NETWORK_CLIENT_LIST_PLAYER_NAME_TOOLTIP), SetAlignment(SA_VERT_CENTER | SA_RIGHT), + NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_CL_CLIENT_NAME_EDIT), SetMinimalSize(12, 14), SetDataTip(SPR_RENAME, STR_NETWORK_CLIENT_LIST_PLAYER_NAME_EDIT_TOOLTIP), + EndContainer(), + EndContainer(), + NWidget(NWID_HORIZONTAL), + NWidget(WWT_MATRIX, COLOUR_GREY, WID_CL_MATRIX), SetMinimalSize(180, 0), SetResize(1, 1), SetFill(1, 1), SetMatrixDataTip(1, 0, STR_NULL), SetScrollbar(WID_CL_SCROLLBAR), + NWidget(NWID_VERTICAL), + NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_CL_SCROLLBAR), + NWidget(WWT_RESIZEBOX, COLOUR_GREY), + EndContainer(), + EndContainer(), + EndContainer(), }; static WindowDesc _client_list_desc( - WDP_AUTO, "list_clients", 0, 0, + WDP_AUTO, "list_clients", 220, 300, WC_CLIENT_LIST, WC_NONE, 0, _nested_client_list_widgets, lengthof(_nested_client_list_widgets) ); /** + * Button shown for either a company or client in the client-list. + * + * These buttons are dynamic and strongly depends on which company/client + * what buttons are available. This class allows dynamically creating them + * as the current Widget system does not. + */ +class ButtonCommon { +public: + SpriteID sprite; ///< The sprite to use on the button. + StringID tooltip; ///< The tooltip of the button. + Colours colour; ///< The colour of the button. + bool disabled; ///< Is the button disabled? + uint height; ///< Calculated height of the button. + uint width; ///< Calculated width of the button. + + ButtonCommon(SpriteID sprite, StringID tooltip, Colours colour) : + sprite(sprite), + tooltip(tooltip), + colour(colour), + disabled(false) + { + Dimension d = GetSpriteSize(sprite); + this->height = d.height + ScaleGUITrad(WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM); + this->width = d.width + ScaleGUITrad(WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT); + } + virtual ~ButtonCommon() {} + + /** + * OnClick handler for when the button is pressed. + */ + virtual void OnClick(struct NetworkClientListWindow *w, Point pt) = 0; +}; + +/** + * Template version of Button, with callback support. + */ +template<typename T> +class Button : public ButtonCommon { +private: + typedef void (*ButtonCallback)(struct NetworkClientListWindow *w, Point pt, T id); ///< Callback function to call on click. + T id; ///< ID this button belongs to. + ButtonCallback proc; ///< Callback proc to call when button is pressed. + +public: + Button(SpriteID sprite, StringID tooltip, Colours colour, T id, ButtonCallback proc) : + ButtonCommon(sprite, tooltip, colour), + id(id), + proc(proc) + { + assert(proc != nullptr); + } + + void OnClick(struct NetworkClientListWindow *w, Point pt) override + { + if (this->disabled) return; + + this->proc(w, pt, this->id); + } +}; + +using CompanyButton = Button<CompanyID>; +using ClientButton = Button<ClientID>; + +/** * Main handle for clientlist */ struct NetworkClientListWindow : Window { - int selected_item; +private: + ClientListWidgets query_widget; ///< During a query this tracks what widget caused the query. + CompanyID join_company; ///< During query for company password, this stores what company we wanted to join. - uint server_client_width; - uint line_height; + Scrollbar *vscroll; ///< Vertical scrollbar of this window. + uint line_height; ///< Current lineheight of each entry in the matrix. + uint line_count; ///< Amount of lines in the matrix. - Dimension icon_size; + std::map<uint, std::vector<std::unique_ptr<ButtonCommon>>> buttons; ///< Per line which buttons are available. - NetworkClientListWindow(WindowDesc *desc, WindowNumber window_number) : - Window(desc), - selected_item(-1) + static const int CLIENT_OFFSET_LEFT = 12; ///< Offset of client entries compared to company entries. + + /** + * Chat button on a Company is clicked. + * @param w The instance of this window. + * @param pt The point where this button was clicked. + * @param company_id The company this button was assigned to. + */ + static void OnClickCompanyChat(NetworkClientListWindow *w, Point pt, CompanyID company_id) { - this->InitNested(window_number); + ShowNetworkChatQueryWindow(DESTTYPE_TEAM, company_id); + } + + /** + * Join button on a Company is clicked. + * @param w The instance of this window. + * @param pt The point where this button was clicked. + * @param company_id The company this button was assigned to. + */ + static void OnClickCompanyJoin(NetworkClientListWindow *w, Point pt, CompanyID company_id) + { + if (_network_server) { + NetworkServerDoMove(CLIENT_ID_SERVER, company_id); + MarkWholeScreenDirty(); + } else if (NetworkCompanyIsPassworded(company_id)) { + w->query_widget = WID_CL_COMPANY_JOIN; + w->join_company = company_id; + ShowQueryString(STR_EMPTY, STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION, NETWORK_PASSWORD_LENGTH, w, CS_ALPHANUMERAL, QSF_PASSWORD); + } else { + NetworkClientRequestMove(company_id); + } } /** - * Finds the amount of clients and set the height correct + * Admin button on a Client is clicked. + * @param w The instance of this window. + * @param pt The point where this button was clicked. + * @param client_id The client this button was assigned to. */ - bool CheckClientListHeight() + static void OnClickClientAdmin(NetworkClientListWindow *w, Point pt, ClientID client_id) { - int num = 0; + PopupClientList(client_id, pt.x + w->left, pt.y + w->top); + } - /* Should be replaced with a loop through all clients */ + /** + * Chat button on a Client is clicked. + * @param w The instance of this window. + * @param pt The point where this button was clicked. + * @param client_id The client this button was assigned to. + */ + static void OnClickClientChat(NetworkClientListWindow *w, Point pt, ClientID client_id) + { + ShowNetworkChatQueryWindow(DESTTYPE_CLIENT, client_id); + } + + /** + * Part of RebuildList() to create the information for a single company. + * @param company_id The company to build the list for. + * @param own_ci The NetworkClientInfo of the client itself. + */ + void RebuildListCompany(CompanyID company_id, const NetworkClientInfo *own_ci) + { + ButtonCommon *chat_button = new CompanyButton(SPR_CHAT, company_id == COMPANY_SPECTATOR ? STR_NETWORK_CLIENT_LIST_CHAT_SPECTATOR_TOOLTIP : STR_NETWORK_CLIENT_LIST_CHAT_COMPANY_TOOLTIP, COLOUR_ORANGE, company_id, &NetworkClientListWindow::OnClickCompanyChat); + + this->buttons[line_count].emplace_back(chat_button); + if (own_ci->client_playas != company_id) this->buttons[line_count].emplace_back(new CompanyButton(SPR_JOIN, STR_NETWORK_CLIENT_LIST_JOIN_TOOLTIP, COLOUR_ORANGE, company_id, &NetworkClientListWindow::OnClickCompanyJoin)); + + this->line_count += 1; + + bool has_players = false; for (const NetworkClientInfo *ci : NetworkClientInfo::Iterate()) { - if (ci->client_playas != COMPANY_INACTIVE_CLIENT) num++; + if (ci->client_playas != company_id) continue; + has_players = true; + + if (_network_own_client_id != ci->client_id) this->buttons[line_count].emplace_back(new ClientButton(SPR_CHAT, STR_NETWORK_CLIENT_LIST_CHAT_CLIENT_TOOLTIP, COLOUR_ORANGE, ci->client_id, &NetworkClientListWindow::OnClickClientChat)); + if (_network_server && _network_own_client_id != ci->client_id) this->buttons[line_count].emplace_back(new ClientButton(SPR_ADMIN, STR_NETWORK_CLIENT_LIST_ADMIN_TOOLTIP, COLOUR_RED, ci->client_id, &NetworkClientListWindow::OnClickClientAdmin)); + + this->line_count += 1; + } + + chat_button->disabled = !has_players; + } + + /** + * Rebuild the list, meaning: calculate the lines needed and what buttons go on which line. + */ + void RebuildList() + { + const NetworkClientInfo *own_ci = NetworkClientInfo::GetByClientID(_network_own_client_id); + + this->buttons.clear(); + this->line_count = 0; + + /* Companies */ + for (const Company *c : Company::Iterate()) { + this->RebuildListCompany(c->index, own_ci); } - num *= this->line_height; + /* Spectators */ + this->RebuildListCompany(COMPANY_SPECTATOR, own_ci); + + this->vscroll->SetCount(this->line_count); + } + + /** + * Get the button at a specific point on the WID_CL_MATRIX. + * @param pt The point to look for a button. + * @return The button or a nullptr if there was none. + */ + ButtonCommon *GetButtonAtPoint(Point pt) + { + uint index = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_CL_MATRIX); + NWidgetBase *widget_matrix = this->GetWidget<NWidgetBase>(WID_CL_MATRIX); - int diff = (num + WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM) - (this->GetWidget<NWidgetBase>(WID_CL_PANEL)->current_y); - /* If height is changed */ - if (diff != 0) { - ResizeWindow(this, 0, diff, false); - return false; + bool rtl = _current_text_dir == TD_RTL; + uint x = rtl ? (uint)widget_matrix->pos_x + WD_FRAMERECT_LEFT : widget_matrix->current_x - WD_FRAMERECT_RIGHT; + + /* Find the buttons for this row. */ + auto button_find = this->buttons.find(index); + if (button_find == this->buttons.end()) return nullptr; + + /* Check if we want to display a tooltip for any of the buttons. */ + for (auto &button : button_find->second) { + uint left = rtl ? x : x - button->width; + uint right = rtl ? x + button->width : x; + + if (IsInsideMM(pt.x, left, right)) { + return button.get(); + } + + int width = button->width + WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT; + x += rtl ? width : -width; } - return true; + + return nullptr; + } + +public: + NetworkClientListWindow(WindowDesc *desc, WindowNumber window_number) : + Window(desc) + { + this->CreateNestedTree(); + this->vscroll = this->GetScrollbar(WID_CL_SCROLLBAR); + this->OnInvalidateData(); + this->FinishInitNested(window_number); + } + + void OnInvalidateData(int data = 0, bool gui_scope = true) override + { + this->RebuildList(); + + /* Currently server information is not sync'd to clients, so we cannot show it on clients. */ + this->GetWidget<NWidgetStacked>(WID_CL_SERVER_SELECTOR)->SetDisplayedPlane(_network_server ? 0 : SZSP_HORIZONTAL); } void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override { - if (widget != WID_CL_PANEL) return; + switch (widget) { + case WID_CL_SERVER_VISIBILITY: + *size = maxdim(GetStringBoundingBox(_server_visibility_dropdown[0]), GetStringBoundingBox(_server_visibility_dropdown[1])); + size->width += padding.width; + size->height += padding.height; + break; - this->server_client_width = std::max(GetStringBoundingBox(STR_NETWORK_SERVER).width, GetStringBoundingBox(STR_NETWORK_CLIENT).width) + WD_FRAMERECT_RIGHT; - this->icon_size = GetSpriteSize(SPR_COMPANY_ICON); - this->line_height = std::max(this->icon_size.height + 2U, (uint)FONT_HEIGHT_NORMAL); + case WID_CL_MATRIX: { + uint height = std::max({GetSpriteSize(SPR_COMPANY_ICON).height, GetSpriteSize(SPR_JOIN).height, GetSpriteSize(SPR_ADMIN).height, GetSpriteSize(SPR_CHAT).height}); + height += ScaleGUITrad(WD_FRAMERECT_TOP) + ScaleGUITrad(WD_FRAMERECT_BOTTOM); + this->line_height = std::max(height, (uint)FONT_HEIGHT_NORMAL) + ScaleGUITrad(WD_MATRIX_TOP + WD_MATRIX_BOTTOM); - uint width = 100; // Default width - for (const NetworkClientInfo *ci : NetworkClientInfo::Iterate()) { - width = std::max(width, GetStringBoundingBox(ci->client_name).width); + resize->width = 1; + resize->height = this->line_height; + fill->height = this->line_height; + size->height = std::max(size->height, 5 * this->line_height); + break; + } } + } - size->width = WD_FRAMERECT_LEFT + this->server_client_width + this->icon_size.width + WD_FRAMERECT_LEFT + width + WD_FRAMERECT_RIGHT; + void OnResize() override + { + this->vscroll->SetCapacityFromWidget(this, WID_CL_MATRIX); } - void OnPaint() override + void SetStringParameters(int widget) const override { - /* Check if we need to reset the height */ - if (!this->CheckClientListHeight()) return; + switch (widget) { + case WID_CL_SERVER_NAME: + SetDParamStr(0, _settings_client.network.server_name); + break; - this->DrawWidgets(); + case WID_CL_SERVER_VISIBILITY: + SetDParam(0, _server_visibility_dropdown[_settings_client.network.server_advertise]); + break; + + case WID_CL_CLIENT_NAME: + SetDParamStr(0, _settings_client.network.client_name); + break; + } } - void DrawWidget(const Rect &r, int widget) const override + void OnClick(Point pt, int widget, int click_count) override { - if (widget != WID_CL_PANEL) return; + switch (widget) { + case WID_CL_SERVER_NAME_EDIT: + if (!_network_server) break; - bool rtl = _current_text_dir == TD_RTL; - int icon_offset = (this->line_height - icon_size.height) / 2; - int text_offset = (this->line_height - FONT_HEIGHT_NORMAL) / 2; + this->query_widget = WID_CL_SERVER_NAME_EDIT; + SetDParamStr(0, _settings_client.network.server_name); + ShowQueryString(STR_JUST_RAW_STRING, STR_NETWORK_CLIENT_LIST_SERVER_NAME_QUERY_CAPTION, NETWORK_NAME_LENGTH, this, CS_ALPHANUMERAL, QSF_LEN_IN_CHARS); + break; - uint y = r.top + WD_FRAMERECT_TOP; - uint left = r.left + WD_FRAMERECT_LEFT; - uint right = r.right - WD_FRAMERECT_RIGHT; - uint type_icon_width = this->server_client_width + this->icon_size.width + WD_FRAMERECT_LEFT; + case WID_CL_CLIENT_NAME_EDIT: + this->query_widget = WID_CL_CLIENT_NAME_EDIT; + SetDParamStr(0, _settings_client.network.client_name); + ShowQueryString(STR_JUST_RAW_STRING, STR_NETWORK_CLIENT_LIST_PLAYER_NAME_QUERY_CAPTION, NETWORK_CLIENT_NAME_LENGTH, this, CS_ALPHANUMERAL, QSF_LEN_IN_CHARS); + break; + case WID_CL_SERVER_VISIBILITY: + if (!_network_server) break; - uint type_left = rtl ? right - this->server_client_width : left; - uint type_right = rtl ? right : left + this->server_client_width - 1; - uint icon_left = rtl ? right - type_icon_width + WD_FRAMERECT_LEFT : left + this->server_client_width; - uint name_left = rtl ? left : left + type_icon_width; - uint name_right = rtl ? right - type_icon_width : right; + ShowDropDownMenu(this, _server_visibility_dropdown, _settings_client.network.server_advertise, WID_CL_SERVER_VISIBILITY, 0, 0); + break; - int i = 0; - for (const NetworkClientInfo *ci : NetworkClientInfo::Iterate()) { - TextColour colour; - if (this->selected_item == i++) { // Selected item, highlight it - GfxFillRect(r.left + 1, y, r.right - 1, y + this->line_height - 1, PC_BLACK); - colour = TC_WHITE; - } else { - colour = TC_BLACK; + case WID_CL_MATRIX: { + ButtonCommon *button = this->GetButtonAtPoint(pt); + if (button == nullptr) break; + + button->OnClick(this, pt); + break; } + } + } - if (ci->client_id == CLIENT_ID_SERVER) { - DrawString(type_left, type_right, y + text_offset, STR_NETWORK_SERVER, colour); - } else { - DrawString(type_left, type_right, y + text_offset, STR_NETWORK_CLIENT, colour); + bool OnTooltip(Point pt, int widget, TooltipCloseCondition close_cond) override + { + switch (widget) { + case WID_CL_MATRIX: { + ButtonCommon *button = this->GetButtonAtPoint(pt); + if (button == nullptr) return false; + + GuiShowTooltips(this, button->tooltip, 0, nullptr, close_cond); + return true; + }; + } + + return false; + } + + void OnDropdownSelect(int widget, int index) override + { + switch (widget) { + case WID_CL_SERVER_VISIBILITY: + if (!_network_server) break; + + _settings_client.network.server_advertise = (index != 0); + break; + + default: + NOT_REACHED(); + } + + this->SetDirty(); + } + + void OnQueryTextFinished(char *str) override + { + if (str == nullptr) return; + + switch (this->query_widget) { + default: NOT_REACHED(); + + case WID_CL_SERVER_NAME_EDIT: { + if (!_network_server) break; + + uint index; + GetSettingFromName("network.server_name", &index); + SetSettingValue(index, StrEmpty(str) ? "Unnamed Server" : str); + this->InvalidateData(); + break; } - /* Filter out spectators */ - if (Company::IsValidID(ci->client_playas)) DrawCompanyIcon(ci->client_playas, icon_left, y + icon_offset); + case WID_CL_CLIENT_NAME_EDIT: { + if (!NetworkValidateClientName(str)) break; - DrawString(name_left, name_right, y + text_offset, ci->client_name, colour); + uint index; + GetSettingFromName("network.client_name", &index); + SetSettingValue(index, str); + this->InvalidateData(); + break; + } - y += line_height; + case WID_CL_COMPANY_JOIN: + NetworkClientRequestMove(this->join_company, str); + break; } } - void OnClick(Point pt, int widget, int click_count) override + /** + * Draw the buttons for a single line in the matrix. + * + * The x-position in RTL is the most left or otherwise the most right pixel + * we can draw the buttons from. + * + * @param x The x-position to start with the buttons. Updated during this function. + * @param y The y-position to start with the buttons. + * @param buttons The buttons to draw. + */ + void DrawButtons(uint &x, uint y, const std::vector<std::unique_ptr<ButtonCommon>> &buttons) const { - /* Show the popup with option */ - if (this->selected_item != -1) { - int client_no = this->selected_item; - for (NetworkClientInfo *ci : NetworkClientInfo::Iterate()) { - if (client_no == 0) { - PopupClientList(ci->client_id, pt.x + this->left, pt.y + this->top); - break; - } - client_no--; + for (auto &button : buttons) { + bool rtl = _current_text_dir == TD_RTL; + + uint left = rtl ? x : x - button->width; + uint right = rtl ? x + button->width : x; + + int offset = std::max(0, ((int)(this->line_height + 1) - (int)button->height) / 2); + + DrawFrameRect(left, y + offset, right, y + offset + button->height, button->colour, FR_NONE); + DrawSprite(button->sprite, PAL_NONE, left + ScaleGUITrad(WD_FRAMERECT_LEFT), y + offset + ScaleGUITrad(WD_FRAMERECT_TOP)); + if (button->disabled) { + GfxFillRect(left + 1, y + offset + 1, right - 1, y + offset + button->height - 1, _colour_gradient[button->colour & 0xF][2], FILLRECT_CHECKER); } + + int width = button->width + WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT; + x += rtl ? width : -width; } } - void OnMouseOver(Point pt, int widget) override + /** + * Draw a company and its clients on the matrix. + * @param c The company to draw. + * @param left The most left pixel of the line. + * @param right The most right pixel of the line. + * @param top The top of the first line. + * @param line The Nth line we are drawing. Updated during this function. + */ + void DrawCompany(const Company *c, uint left, uint right, uint top, uint &line) const { - /* -1 means we left the current window */ - if (pt.y == -1) { - this->selected_item = -1; - this->SetDirty(); - return; + bool rtl = _current_text_dir == TD_RTL; + int text_y_offset = std::max(0, ((int)(this->line_height + 1) - (int)FONT_HEIGHT_NORMAL) / 2) + WD_MATRIX_BOTTOM; + + Dimension d = GetSpriteSize(SPR_COMPANY_ICON); + int offset = std::max(0, ((int)(this->line_height + 1) - (int)d.height) / 2); + + uint text_left = left + (rtl ? (uint)WD_FRAMERECT_LEFT : d.width + 8); + uint text_right = right - (rtl ? d.width + 8 : (uint)WD_FRAMERECT_RIGHT); + + uint line_start = this->vscroll->GetPosition(); + uint line_end = line_start + this->vscroll->GetCapacity(); + + uint y = top + (this->line_height * (line - line_start)); + + /* Draw the company line (if in range of scrollbar). */ + if (IsInsideMM(line, line_start, line_end)) { + uint x = rtl ? text_left : text_right; + + /* If there are buttons for this company, draw them. */ + auto button_find = this->buttons.find(line); + if (button_find != this->buttons.end()) { + this->DrawButtons(x, y, button_find->second); + } + + if (c == nullptr) { + DrawSprite(SPR_COMPANY_ICON, PALETTE_TO_GREY, rtl ? right - d.width - 4 : left + 4, y + offset); + DrawString(rtl ? x : text_left, rtl ? text_right : x, y + text_y_offset, STR_NETWORK_CLIENT_LIST_SPECTATORS, TC_SILVER); + } else { + DrawCompanyIcon(c->index, rtl ? right - d.width - 4 : left + 4, y + offset); + + SetDParam(0, c->index); + SetDParam(1, c->index); + DrawString(rtl ? x : text_left, rtl ? text_right : x, y + text_y_offset, STR_COMPANY_NAME, TC_SILVER); + } } - /* Find the new selected item (if any) */ - pt.y -= this->GetWidget<NWidgetBase>(WID_CL_PANEL)->pos_y; - int item = -1; - if (IsInsideMM(pt.y, WD_FRAMERECT_TOP, this->GetWidget<NWidgetBase>(WID_CL_PANEL)->current_y - WD_FRAMERECT_BOTTOM)) { - item = (pt.y - WD_FRAMERECT_TOP) / this->line_height; + y += this->line_height; + line++; + + for (const NetworkClientInfo *ci : NetworkClientInfo::Iterate()) { + if (c != nullptr && ci->client_playas != c->index) continue; + if (c == nullptr && ci->client_playas != COMPANY_SPECTATOR) continue; + + /* Draw the player line (if in range of scrollbar). */ + if (IsInsideMM(line, line_start, line_end)) { + uint x = rtl ? text_left : text_right; + + /* If there are buttons for this client, draw them. */ + auto button_find = this->buttons.find(line); + if (button_find != this->buttons.end()) { + this->DrawButtons(x, y, button_find->second); + } + + StringID client_string = STR_JUST_RAW_STRING; + + if (ci->client_id == CLIENT_ID_SERVER) { + client_string = STR_NETWORK_CLIENT_LIST_PLAYER_HOST; + } + if (ci->client_id == _network_own_client_id) { + client_string = STR_NETWORK_CLIENT_LIST_PLAYER_SELF; + } + + SetDParamStr(0, ci->client_name); + DrawString(rtl ? x : text_left + CLIENT_OFFSET_LEFT, rtl ? text_right - CLIENT_OFFSET_LEFT : x, y + text_y_offset, client_string, TC_BLACK); + } + + y += this->line_height; + line++; } + } + + void DrawWidget(const Rect &r, int widget) const override + { + switch (widget) { + case WID_CL_MATRIX: { + uint line = 0; - /* It did not change.. no update! */ - if (item == this->selected_item) return; - this->selected_item = item; + for (const Company *c : Company::Iterate()) { + this->DrawCompany(c, r.left, r.right, r.top, line); + } + /* Specators */ + this->DrawCompany(nullptr, r.left, r.right, r.top, line); - /* Repaint */ - this->SetDirty(); + break; + } + } } }; diff --git a/src/network/network_server.cpp b/src/network/network_server.cpp index a0d1a0066..746077abd 100644 --- a/src/network/network_server.cpp +++ b/src/network/network_server.cpp @@ -288,13 +288,14 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::CloseConnection(NetworkRecvSta _network_clients_connected--; DeleteWindowById(WC_CLIENT_LIST_POPUP, this->client_id); - SetWindowDirty(WC_CLIENT_LIST, 0); this->SendPackets(true); delete this->GetInfo(); delete this; + InvalidateWindowData(WC_CLIENT_LIST, 0); + return status; } @@ -1043,6 +1044,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_MAP_OK(Packet * this->GetClientName(client_name, lastof(client_name)); NetworkTextMessage(NETWORK_ACTION_JOIN, CC_DEFAULT, false, client_name, nullptr, this->client_id); + InvalidateWindowData(WC_CLIENT_LIST, 0); /* Mark the client as pre-active, and wait for an ACK * so we know he is done loading and in sync with us */ @@ -2061,6 +2063,9 @@ void NetworkServerDoMove(ClientID client_id, CompanyID company_id) NetworkAction action = (company_id == COMPANY_SPECTATOR) ? NETWORK_ACTION_COMPANY_SPECTATOR : NETWORK_ACTION_COMPANY_JOIN; NetworkServerSendChat(action, DESTTYPE_BROADCAST, 0, "", client_id, company_id + 1); + + InvalidateWindowClassesData(WC_CLIENT_LIST_POPUP); + InvalidateWindowData(WC_CLIENT_LIST, 0); } /** diff --git a/src/table/sprites.h b/src/table/sprites.h index 9071e61dc..039d50b9d 100644 --- a/src/table/sprites.h +++ b/src/table/sprites.h @@ -54,7 +54,7 @@ static const SpriteID SPR_LARGE_SMALL_WINDOW = 682; /** Extra graphic spritenumbers */ static const SpriteID SPR_OPENTTD_BASE = 4896; -static const uint16 OPENTTD_SPRITE_COUNT = 186; +static const uint16 OPENTTD_SPRITE_COUNT = 189; /* Halftile-selection sprites */ static const SpriteID SPR_HALFTILE_SELECTION_FLAT = SPR_OPENTTD_BASE; @@ -166,6 +166,10 @@ static const SpriteID SPR_WINDOW_DEFSIZE = SPR_OPENTTD_BASE + 168; static const SpriteID SPR_RENAME = SPR_OPENTTD_BASE + 184; static const SpriteID SPR_GOTO_LOCATION = SPR_OPENTTD_BASE + 185; +static const SpriteID SPR_CHAT = SPR_OPENTTD_BASE + 186; +static const SpriteID SPR_ADMIN = SPR_OPENTTD_BASE + 187; +static const SpriteID SPR_JOIN = SPR_OPENTTD_BASE + 188; + static const SpriteID SPR_IMG_CARGOFLOW = SPR_OPENTTD_BASE + 174; static const SpriteID SPR_SIGNALS_BASE = SPR_OPENTTD_BASE + OPENTTD_SPRITE_COUNT; diff --git a/src/toolbar_gui.cpp b/src/toolbar_gui.cpp index ea50661e4..6a65382b4 100644 --- a/src/toolbar_gui.cpp +++ b/src/toolbar_gui.cpp @@ -205,8 +205,7 @@ static void PopupMainToolbMenu(Window *w, int widget, StringID string, int count /** Enum for the Company Toolbar's network related buttons */ static const int CTMN_CLIENT_LIST = -1; ///< Show the client list static const int CTMN_NEW_COMPANY = -2; ///< Create a new company -static const int CTMN_SPECTATE = -3; ///< Become spectator -static const int CTMN_SPECTATOR = -4; ///< Show a company window as spectator +static const int CTMN_SPECTATOR = -3; ///< Show a company window as spectator /** * Pop up a generic company list menu. @@ -227,8 +226,6 @@ static void PopupMainCompanyToolbMenu(Window *w, int widget, int grey = 0) if (_local_company == COMPANY_SPECTATOR) { list.emplace_back(new DropDownListStringItem(STR_NETWORK_COMPANY_LIST_NEW_COMPANY, CTMN_NEW_COMPANY, NetworkMaxCompaniesReached())); - } else { - list.emplace_back(new DropDownListStringItem(STR_NETWORK_COMPANY_LIST_SPECTATE, CTMN_SPECTATE, NetworkMaxSpectatorsReached())); } break; @@ -619,15 +616,6 @@ static CallBackFunction MenuClickCompany(int index) NetworkSendCommand(0, CCA_NEW, 0, CMD_COMPANY_CTRL, nullptr, nullptr, _local_company); } return CBF_NONE; - - case CTMN_SPECTATE: - if (_network_server) { - NetworkServerDoMove(CLIENT_ID_SERVER, COMPANY_SPECTATOR); - MarkWholeScreenDirty(); - } else { - NetworkClientRequestMove(COMPANY_SPECTATOR); - } - return CBF_NONE; } } ShowCompany((CompanyID)index); diff --git a/src/widgets/network_widget.h b/src/widgets/network_widget.h index 79d33fb06..a453b085c 100644 --- a/src/widgets/network_widget.h +++ b/src/widgets/network_widget.h @@ -96,7 +96,16 @@ enum NetworkLobbyWidgets { /** Widgets of the #NetworkClientListWindow class. */ enum ClientListWidgets { - WID_CL_PANEL, ///< Panel of the window. + WID_CL_PANEL, ///< Panel of the window. + WID_CL_SERVER_SELECTOR, ///< Selector to hide the server frame. + WID_CL_SERVER_NAME, ///< Server name. + WID_CL_SERVER_NAME_EDIT, ///< Edit button for server name. + WID_CL_SERVER_VISIBILITY, ///< Server visibility. + WID_CL_CLIENT_NAME, ///< Client name. + WID_CL_CLIENT_NAME_EDIT, ///< Edit button for client name. + WID_CL_MATRIX, ///< Company/client list. + WID_CL_SCROLLBAR, ///< Scrollbar for company/client list. + WID_CL_COMPANY_JOIN, ///< Used for QueryWindow when a company has a password. }; /** Widgets of the #NetworkClientListPopupWindow class. */ |