From 2c5f48943c7e1505c3e757597eb03d2ef62893b8 Mon Sep 17 00:00:00 2001 From: dP Date: Wed, 29 Sep 2021 22:55:14 +0300 Subject: [PATCH] Add client list overlay (toggleable) --- src/citymania/CMakeLists.txt | 2 + src/citymania/cm_client_list_gui.cpp | 205 ++++++++++++++++++++++ src/citymania/cm_client_list_gui.hpp | 12 ++ src/gfx.cpp | 4 + src/lang/english.txt | 1 + src/network/network.cpp | 4 +- src/network/network_client.cpp | 6 + src/network/network_gui.cpp | 9 + src/network/network_server.cpp | 5 + src/settings_type.h | 1 + src/table/settings/citymania_settings.ini | 4 + src/widgets/network_widget.h | 1 + src/window.cpp | 2 + 13 files changed, 255 insertions(+), 1 deletion(-) create mode 100644 src/citymania/cm_client_list_gui.cpp create mode 100644 src/citymania/cm_client_list_gui.hpp diff --git a/src/citymania/CMakeLists.txt b/src/citymania/CMakeLists.txt index e1eb7f26d4..1459c573ea 100644 --- a/src/citymania/CMakeLists.txt +++ b/src/citymania/CMakeLists.txt @@ -24,6 +24,8 @@ add_files( cm_blueprint.cpp cm_cargo_table_gui.hpp cm_cargo_table_gui.cpp + cm_client_list_gui.hpp + cm_client_list_gui.cpp cm_commands_gui.hpp cm_commands_gui.cpp cm_highlight.hpp diff --git a/src/citymania/cm_client_list_gui.cpp b/src/citymania/cm_client_list_gui.cpp new file mode 100644 index 0000000000..d73d163087 --- /dev/null +++ b/src/citymania/cm_client_list_gui.cpp @@ -0,0 +1,205 @@ +#include "../stdafx.h" + +#include "cm_client_list_gui.hpp" + +#include "../blitter/factory.hpp" +#include "../company_base.h" +#include "../company_gui.h" +#include "../console_func.h" +#include "../debug.h" +#include "../core/geometry_type.hpp" +#include "../gfx_func.h" +#include "../network/network.h" +#include "../network/network_base.h" +#include "../network/network_func.h" +#include "../strings_func.h" +#include "../table/sprites.h" +#include "../video/video_driver.hpp" +#include "../window_func.h" +#include "../window_gui.h" +#include "../zoom_func.h" + +#include "../safeguards.h" + +namespace citymania { + +bool Intersects(PointDimension rect, Point pos, Point size) { + return ( + pos.x + size.x >= rect.x && + pos.x <= rect.x + rect.width && + pos.y + size.y >= rect.y && + pos.y <= rect.y + rect.height + ); +} + +bool Intersects(PointDimension rect, int left, int top, int right, int bottom) { + return ( + right >= rect.x && + left <= rect.x + rect.width && + bottom >= rect.y && + top <= rect.y + rect.height + ); +} + + +class ClientListOverlay { +protected: + PointDimension box; + bool drawn = false; + bool dirty = true; + PointDimension backup_box; + uint8 *backup = nullptr; + int backup_size = -1; + int padding; + int line_height; + int text_offset_y; + int icon_offset_y; + +public: + void SetDirty() { + this->dirty = true; + } + void UpdateSize() { + this->padding = ScaleGUITrad(3); + auto icon_size = GetSpriteSize(SPR_COMPANY_ICON); + auto common_height = std::max(icon_size.height, FONT_HEIGHT_NORMAL); + this->line_height = common_height + this->padding; + this->text_offset_y = (common_height - FONT_HEIGHT_NORMAL) / 2; + this->icon_offset_y = (common_height - icon_size.height) / 2; + this->box.height = this->padding; + this->box.width = 0; + for (const NetworkClientInfo *ci : NetworkClientInfo::Iterate()) { + SetDParamStr(0, ci->client_name); + this->box.width = std::max(this->box.width, GetStringBoundingBox(STR_JUST_RAW_STRING).width); + this->box.height += this->line_height; + } + this->box.width += this->padding * 3 + icon_size.width; + this->box.x = this->padding; + this->box.y = this->padding; + Window *toolbar = FindWindowById(WC_MAIN_TOOLBAR, 0); + if (toolbar != nullptr && toolbar->left < this->box.x + this->box.width + this->padding) { + this->box.y = toolbar->top + toolbar->height + this->padding; + } + + if (this->box.x + this->box.width > _screen.width) + this->box.width = _screen.width - this->box.x; + if (this->box.y + this->box.height > _screen.height) + this->box.height = _screen.height - this->box.y; + } + + void Undraw(int left, int top, int right, int bottom) { + auto &box = this->backup_box; + if (!this->drawn) return; + + if (!Intersects(box, left, top, right, bottom)) return; + + if (box.x + box.width > _screen.width) return; + if (box.y + box.height > _screen.height) return; + + Blitter *blitter = BlitterFactory::GetCurrentBlitter(); + + /* Put our 'shot' back to the screen */ + blitter->CopyFromBuffer(blitter->MoveTo(_screen.dst_ptr, box.x, box.y), this->backup, box.width, box.height); + /* And make sure it is updated next time */ + VideoDriver::GetInstance()->MakeDirty(box.x, box.y, box.width, box.height); + + this->drawn = false; + this->dirty = true; + } + + void Draw() { + if (!this->dirty) return; + this->dirty = false; + + this->UpdateSize(); + Blitter *blitter = BlitterFactory::GetCurrentBlitter(); + + bool make_backup = true; + + if (this->drawn) { + /* Put our 'shot' back to the screen */ + blitter->CopyFromBuffer(blitter->MoveTo(_screen.dst_ptr, this->backup_box.x, this->backup_box.y), this->backup, this->backup_box.width, this->backup_box.height); + /* And make sure it is updated next time */ + VideoDriver::GetInstance()->MakeDirty(this->backup_box.x, this->backup_box.y, this->backup_box.width, this->backup_box.height); + + this->drawn = false; + + make_backup = ( + this->box.x != this->backup_box.x || + this->box.y != this->backup_box.y || + this->box.width != this->backup_box.width || + this->box.height != this->backup_box.height + ); + } + + if (_game_mode != GM_NORMAL) return; + if (_iconsole_mode != ICONSOLE_CLOSED) return; + if (!_networking) return; + if (!_settings_client.gui.cm_show_client_overlay) return; + if (this->box.width <= 0 || this->box.height <= 0) return; + + if (make_backup) { + auto size = this->box.width * this->box.height * BlitterFactory::GetCurrentBlitter()->GetBytesPerPixel(); + if (size > this->backup_size) { + this->backup = ReallocT(this->backup, size); + this->backup_size = size; + } + blitter->CopyToBuffer(blitter->MoveTo(_screen.dst_ptr, this->box.x, this->box.y), this->backup, this->box.width, this->box.height); + this->backup_box = this->box; + } + + _cur_dpi = &_screen; // switch to _screen painting + + /* Paint a half-transparent box behind the client list */ + GfxFillRect(this->box.x, this->box.y, this->box.x + this->box.width - 1, this->box.y + this->box.height - 1, + PALETTE_TO_TRANSPARENT, FILLRECT_RECOLOUR // black, but with some alpha for background + ); + + int y = this->box.y + this->padding; + int x = this->box.x + this->padding; + + auto text_left = x + GetSpriteSize(SPR_COMPANY_ICON).width + this->padding; + auto text_right = this->box.x + this->box.width - 1 - this->padding; + + std::vector> clients; + for (const NetworkClientInfo *ci : NetworkClientInfo::Iterate()) { + auto colour = TC_SILVER; + if (ci->client_id == _network_own_client_id) colour = TC_WHITE; + if (ci->client_id == CLIENT_ID_SERVER) colour = TC_ORANGE; + clients.emplace_back(ci->client_playas, ci->client_name, colour); + } + std::sort(clients.begin(), clients.end()); + for (const auto &[playas, name, colour] : clients) { + SetDParamStr(0, name); + // auto colour = TC_SILVER; + // if (ci->client_id == _network_own_client_id) colour = TC_WHITE; + // if (ci->client_id == CLIENT_ID_SERVER) colour = TC_ORANGE; + DrawString(text_left, text_right, y + this->text_offset_y, STR_JUST_RAW_STRING, colour); + if (Company::IsValidID(playas)) + DrawCompanyIcon(playas, x, y + this->icon_offset_y); + y += this->line_height; + } + + /* Make sure the data is updated next flush */ + VideoDriver::GetInstance()->MakeDirty(this->box.x, this->box.y, this->box.width, this->box.height); + + this->drawn = true; + } + +}; + +ClientListOverlay _client_list_overlay; + +void SetClientListDirty() { + _client_list_overlay.SetDirty(); +} + +void UndrawClientList(int left, int top, int right, int bottom) { + _client_list_overlay.Undraw(left, top, right, bottom); +} + +void DrawClientList() { + _client_list_overlay.Draw(); +} + +} // namespace citymania diff --git a/src/citymania/cm_client_list_gui.hpp b/src/citymania/cm_client_list_gui.hpp new file mode 100644 index 0000000000..192abb4a51 --- /dev/null +++ b/src/citymania/cm_client_list_gui.hpp @@ -0,0 +1,12 @@ +#ifndef CM_CLIENT_LIST_GUI_HPP +#define CM_CLIENT_LIST_GUI_HPP + +namespace citymania { + +void SetClientListDirty(); +void UndrawClientList(int left, int top, int right, int bottom); +void DrawClientList(); + +} // namespace citymania + +#endif diff --git a/src/gfx.cpp b/src/gfx.cpp index 8ce61efbaf..82541822e9 100644 --- a/src/gfx.cpp +++ b/src/gfx.cpp @@ -26,6 +26,8 @@ #include "table/sprites.h" #include "table/control_codes.h" +#include "citymania/cm_client_list_gui.hpp" + #include "safeguards.h" byte _dirkeys; ///< 1 = left, 2 = up, 4 = right, 8 = down @@ -94,6 +96,7 @@ void GfxScroll(int left, int top, int width, int height, int xo, int yo) if (_cursor.visible) UndrawMouseCursor(); if (_networking) NetworkUndrawChatMessage(); + if (_networking) citymania::UndrawClientList(left, top, left + width, top + height); blitter->ScrollBuffer(_screen.dst_ptr, left, top, width, height, xo, yo); /* This part of the screen is now dirty. */ @@ -1585,6 +1588,7 @@ void RedrawScreenRect(int left, int top, int right, int bottom) } if (_networking) NetworkUndrawChatMessage(); + if (_networking) citymania::UndrawClientList(left, top, right, bottom); DrawOverlappedWindowForAll(left, top, right, bottom); diff --git a/src/lang/english.txt b/src/lang/english.txt index 3be2f4cbff..27a3efd32f 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -5966,3 +5966,4 @@ STR_CONFIG_SETTING_PERSISTENT_DEPOTTOOLS_HELPTEXT :Keep the buildi STR_CM_CONFIG_SETTING_IMPROVED_STATION_JOIN :Use improved station joining controls: {STRING2} STR_CM_CONFIG_SETTING_IMPROVED_STATION_JOIN_HELPTEXT :Use Ctrl-click on station tile to select or deselect station to join. If station has no tiles Ctrl-click its sign. Ctrl-click empty tile for a new station. Also recently built station is automatically selected as a station to join. {RED}Doesn't work if joining stations not directly adjacent(distant join) is not allowed in settings. +CM_STR_TOGGLE_CLIENTS_OVERLAY :Toggle client list overlay. diff --git a/src/network/network.cpp b/src/network/network.cpp index 42df3c3621..6faa73853e 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -39,7 +39,8 @@ #include #include -#include "citymania/cm_console_cmds.hpp" +#include "../citymania/cm_client_list_gui.hpp" +#include "../citymania/cm_console_cmds.hpp" #include "../safeguards.h" @@ -562,6 +563,7 @@ NetworkAddress ParseConnectionString(const std::string &connection_string, uint1 ServerNetworkGameSocketHandler *cs = new ServerNetworkGameSocketHandler(s); cs->client_address = address; // Save the IP of the client + citymania::SetClientListDirty(); InvalidateWindowData(WC_CLIENT_LIST, 0); } diff --git a/src/network/network_client.cpp b/src/network/network_client.cpp index e33d347bae..c890e0961b 100644 --- a/src/network/network_client.cpp +++ b/src/network/network_client.cpp @@ -35,6 +35,7 @@ #include "../town.h" #include "network_func.h" #include "../citymania/cm_newgrf_revisions.hpp" +#include "../citymania/cm_client_list_gui.hpp" #include "../safeguards.h" @@ -590,6 +591,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_CLIENT_INFO(Pac ci->client_playas = playas; ci->client_name = name; + citymania::SetClientListDirty(); InvalidateWindowData(WC_CLIENT_LIST, 0); InvalidateWindowClassesData(WC_WATCH_COMPANY, 0); SetWindowClassesDirty(WC_WATCH_COMPANY); @@ -613,6 +615,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_CLIENT_INFO(Pac ci->client_name = name; + citymania::SetClientListDirty(); InvalidateWindowData(WC_CLIENT_LIST, 0); InvalidateWindowClassesData(WC_WATCH_COMPANY, 0); SetWindowClassesDirty(WC_WATCH_COMPANY); @@ -1009,6 +1012,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_ERROR_QUIT(Pack delete ci; } + citymania::SetClientListDirty(); InvalidateWindowData(WC_CLIENT_LIST, 0); return NETWORK_RECV_STATUS_OKAY; @@ -1028,6 +1032,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_QUIT(Packet *p) Debug(net, 1, "Unknown client ({}) is leaving the game", client_id); } + citymania::SetClientListDirty(); InvalidateWindowData(WC_CLIENT_LIST, 0); InvalidateWindowClassesData( WC_WATCH_COMPANYA, 0 ); SetWindowClassesDirty( WC_WATCH_COMPANYA ); @@ -1047,6 +1052,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_JOIN(Packet *p) NetworkTextMessage(NETWORK_ACTION_JOIN, CC_DEFAULT, false, ci->client_name); } + citymania::SetClientListDirty(); 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 74f6adbb60..8fc4779162 100644 --- a/src/network/network_gui.cpp +++ b/src/network/network_gui.cpp @@ -53,6 +53,7 @@ #include "../error.h" #include "../zoom_func.h" +#include "../citymania/cm_client_list_gui.hpp" #include "../citymania/cm_hotkeys.hpp" #include "../citymania/cm_watch_gui.hpp" @@ -1328,6 +1329,7 @@ static const NWidgetPart _nested_client_list_widgets[] = { NWidget(NWID_HORIZONTAL), NWidget(WWT_CLOSEBOX, COLOUR_GREY), NWidget(WWT_CAPTION, COLOUR_GREY), SetDataTip(STR_NETWORK_CLIENT_LIST_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), + NWidget(WWT_IMGBTN, COLOUR_GREY, CM_WID_CL_TOGGLE_OVERLAY), SetDataTip(SPR_LARGE_SMALL_WINDOW, CM_STR_TOGGLE_CLIENTS_OVERLAY), NWidget(WWT_DEFSIZEBOX, COLOUR_GREY), NWidget(WWT_STICKYBOX, COLOUR_GREY), EndContainer(), @@ -1743,6 +1745,7 @@ public: this->vscroll = this->GetScrollbar(WID_CL_SCROLLBAR); this->OnInvalidateData(); this->FinishInitNested(window_number); + this->GetWidget(CM_WID_CL_TOGGLE_OVERLAY)->SetLowered(_settings_client.gui.cm_show_client_overlay); } void OnInvalidateData(int data = 0, bool gui_scope = true) override @@ -1844,6 +1847,12 @@ public: button->OnClick(this, pt); break; } + case CM_WID_CL_TOGGLE_OVERLAY: { + _settings_client.gui.cm_show_client_overlay = !_settings_client.gui.cm_show_client_overlay; + this->GetWidget(widget)->SetLowered(_settings_client.gui.cm_show_client_overlay); + citymania::SetClientListDirty(); + break; + } } } diff --git a/src/network/network_server.cpp b/src/network/network_server.cpp index e0417c0ec5..eca1b02f74 100644 --- a/src/network/network_server.cpp +++ b/src/network/network_server.cpp @@ -32,6 +32,8 @@ #include #include +#include "../citymania/cm_client_list_gui.hpp" + #include "../safeguards.h" @@ -293,6 +295,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::CloseConnection(NetworkRecvSta delete this->GetInfo(); delete this; + citymania::SetClientListDirty(); InvalidateWindowData(WC_CLIENT_LIST, 0); return status; @@ -977,6 +980,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_MAP_OK(Packet * std::string client_name = this->GetClientName(); NetworkTextMessage(NETWORK_ACTION_JOIN, CC_DEFAULT, false, client_name, "", this->client_id); + citymania::SetClientListDirty(); InvalidateWindowData(WC_CLIENT_LIST, 0); Debug(net, 3, "[{}] Client #{} ({}) joined as {}", ServerNetworkGameSocketHandler::GetName(), this->client_id, this->GetClientIP(), client_name); @@ -1945,6 +1949,7 @@ 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); + citymania::SetClientListDirty(); InvalidateWindowData(WC_CLIENT_LIST, 0); } diff --git a/src/settings_type.h b/src/settings_type.h index 5d1a0a7587..529255ffdf 100644 --- a/src/settings_type.h +++ b/src/settings_type.h @@ -223,6 +223,7 @@ struct GUISettings { uint8 cm_shaded_trees; bool cm_show_apm; uint8 cm_graph_background; + bool cm_show_client_overlay; /** * Returns true when the user has sufficient privileges to edit newgrfs on a running game diff --git a/src/table/settings/citymania_settings.ini b/src/table/settings/citymania_settings.ini index eb3de56337..d461dfb9c0 100644 --- a/src/table/settings/citymania_settings.ini +++ b/src/table/settings/citymania_settings.ini @@ -268,3 +268,7 @@ str = STR_CM_CONFIG_SETTING_IMPROVED_STATION_JOIN strhelp = STR_CM_CONFIG_SETTING_IMPROVED_STATION_JOIN_HELPTEXT cat = SC_BASIC +[SDTC_BOOL] +var = gui.cm_show_client_overlay +def = false +cat = SC_BASIC diff --git a/src/widgets/network_widget.h b/src/widgets/network_widget.h index ac40c23b76..11c9834161 100644 --- a/src/widgets/network_widget.h +++ b/src/widgets/network_widget.h @@ -95,6 +95,7 @@ enum ClientListWidgets { WID_CL_SCROLLBAR, ///< Scrollbar for company/client list. WID_CL_COMPANY_JOIN, ///< Used for QueryWindow when a company has a password. WID_CL_CLIENT_COMPANY_COUNT, ///< Count of clients and companies. + CM_WID_CL_TOGGLE_OVERLAY, ///< (CityMania) toggle client list overlay visibility. }; /** Widgets of the #NetworkJoinStatusWindow class. */ diff --git a/src/window.cpp b/src/window.cpp index 12b840f11d..62927783cf 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -41,6 +41,7 @@ #include "guitimer_func.h" #include "news_func.h" +#include "citymania/cm_client_list_gui.hpp" #include "citymania/cm_hotkeys.hpp" #include "safeguards.h" @@ -3132,6 +3133,7 @@ void UpdateWindows() /* Update viewport only if window is not shaded. */ if (w->viewport != nullptr && !w->IsShaded()) UpdateViewportPosition(w); } + citymania::DrawClientList(); NetworkDrawChatMessage(); /* Redraw mouse cursor in case it was hidden */ DrawMouseCursor();