diff --git a/Engine/Game/Multiplayer/session_model.h b/Engine/Game/Multiplayer/session_model.h index 7381ddae..1bcf7a17 100644 --- a/Engine/Game/Multiplayer/session_model.h +++ b/Engine/Game/Multiplayer/session_model.h @@ -101,12 +101,13 @@ struct MultiplayerChatLine { std::string name, text; bool local{}; // sent by this player // The sender's role, as their nametag shows it: its colour (IM_COL32 layout, 0 = none) - // and a tag shown in a box before the name ("Dev", "Staff", "Creator", "Centrix", "Homie", "Admin", "Host", "Friend" or empty). + // and a tag shown in a box before the name ("Dev", "Staff", "Creator", "Centrix", "Homie", "Admin", "Host", "Friend", "Server" or empty). std::uint32_t color{}; std::string tag; // With the chat filter on, `text` is masked and this is the line as sent (same length), so // the overlay can keep emote names the filter caught; empty when nothing was masked. std::string unmasked; + bool server{}; // said by the dedicated server itself: its console, welcome or answers }; // A command typed into chat with a leading "/" (shown as the player types "/"). struct MultiplayerChatCommand { diff --git a/Extension/Multiplayer/Hud/custom_nametags.h b/Extension/Multiplayer/Hud/custom_nametags.h index 4320cfb6..baa24da0 100644 --- a/Extension/Multiplayer/Hud/custom_nametags.h +++ b/Extension/Multiplayer/Hud/custom_nametags.h @@ -39,7 +39,9 @@ inline constexpr std::uint32_t nametag_white = 0xffffffffU, nametag_developer = nametag_staff = 0xff71cc2eU, // green (#2ECC71) nametag_admin = 0xffc674ffU, // pink nametag_host = 0xffffc85eU, // blue - nametag_friend = 0xff8ae07bU; // green + nametag_friend = 0xff8ae07bU, // green + nametag_server = 0xffff5c8eU, // violet (#8E5CFF) + nametag_server_text = 0xffffc8d9U; // lavender (#D9C8FF), its lines' text // Developers, staff, content creators, homies and Centrix shimmer from these to their role // colour (Centrix between white and its blue). inline constexpr std::uint32_t nametag_developer_start = 0xffff206eU, // #6E20FF, IM_COL32 layout diff --git a/Extension/Multiplayer/Session/session_view.cpp b/Extension/Multiplayer/Session/session_view.cpp index 9b0d447b..1eaeb0d3 100644 --- a/Extension/Multiplayer/Session/session_view.cpp +++ b/Extension/Multiplayer/Session/session_view.cpp @@ -579,7 +579,7 @@ std::pair mark_role(IdentityList list) { std::pair player_role(Session &s, std::uint64_t sender, bool local, bool marks) { if (!sender) return {}; const bool dedicated = dedicated_host(s); - if (dedicated && sender == s.host_id) return {nametag_admin, {}}; // the server itself + if (dedicated && sender == s.host_id) return {nametag_server, "Server"}; // the server itself const auto *peer = local ? nullptr : find_peer(s, sender); const bool vouched = local || (peer && steam_vouched(s, *peer)); // Who the backend says a player is comes before what they are in this lobby, unless they @@ -597,7 +597,13 @@ std::pair player_role(Session &s, std::uint64_t send void add_chat(Session &s, std::uint64_t sender, std::string name, std::string text, bool local, bool marks) { if (name.empty()) name = sender ? "Player" : "ReSkate"; auto [color, tag] = player_role(s, sender, local, marks); - s.chat.push_back({++s.chat_sequence, sender, now_us(), std::move(name), std::move(text), local, color, std::move(tag)}); + // The dedicated server's own lines (its chat, and its answers sent to this player alone) stand out. + const bool server = dedicated_host(s) && (sender ? sender == s.host_id : name == "Server"); + if (server && !sender) { + color = nametag_server; + tag = "Server"; + } + s.chat.push_back({++s.chat_sequence, sender, now_us(), std::move(name), std::move(text), local, color, std::move(tag), {}, server}); while (s.chat.size() > multiplayer_chat_history) s.chat.pop_front(); publish_chat(s); } diff --git a/Extension/UI/Overlay/chat_overlay.cpp b/Extension/UI/Overlay/chat_overlay.cpp index 99b39c67..70e48f23 100644 --- a/Extension/UI/Overlay/chat_overlay.cpp +++ b/Extension/UI/Overlay/chat_overlay.cpp @@ -78,6 +78,7 @@ Clock::time_point arrived(const ChatState& c, std::uint64_t sequence) { // The sender's role colour, as their nametag shows it; ReSkate's notices muted. Lines without // a role (older sessions) fall back to a steady colour per sender. ImU32 name_colour(const MultiplayerChatLine& line) { + if (line.server) return line.color ? line.color : multiplayer::nametag_server; if (!line.sender) return theme::muted; if (line.color) return line.color; if (line.local) return theme::blue; @@ -89,6 +90,11 @@ ImU32 name_colour(const MultiplayerChatLine& line) { return palette[mixed % palette.size()]; } +// A message's own colour: the server's lines in lavender, everyone else's white. +ImU32 text_colour(const MultiplayerChatLine& line) { + return line.server ? multiplayer::nametag_server_text : theme::paper; +} + // "name:"; a role tag is drawn in its own box before it (role_badge.h). std::string chat_label(const MultiplayerChatLine& line) { return line.name + ":"; } @@ -491,6 +497,7 @@ void draw_chat() { ImGui::SameLine(0.0f, 6.0f * scale); const auto& drawn = layout_for(layout, line, ImGui::GetFont(), ImGui::GetFontSize(), line_width, ImGui::GetCursorScreenPos().x - line_start.x); + ImGui::PushStyleColor(ImGuiCol_Text, text_colour(line)); if (drawn.emotes) { draw_rich(ImGui::GetWindowDrawList(), ImGui::GetFont(), ImGui::GetFontSize(), line_start, drawn.text, drawn.rich, ImGui::GetColorU32(ImGuiCol_Text), 1.0f); @@ -499,6 +506,7 @@ void draw_chat() { } else { ImGui::TextUnformatted(drawn.text.c_str()); } + ImGui::PopStyleColor(); } if (c.feed.lines.empty()) { ImGui::PushStyleColor(ImGuiCol_Text, theme::muted); @@ -603,8 +611,9 @@ void draw_chat() { draw->AddText(heading, name_size, ImVec2(at.x + badge, at.y), with_alpha(name_colour(*line), alpha), label.c_str()); shade_nametag_gradient(draw, name_vertices, at.x + badge, name_extent.x, name_colour(*line), ImGui::GetTime()); const ImVec2 text_at = beside ? ImVec2(at.x + indent, at.y) : ImVec2(at.x, at.y + name_extent.y); - if (emotes) draw_rich(draw, body, size, text_at, drawn.text, drawn.rich, with_alpha(theme::paper, alpha), alpha); - else draw->AddText(body, size, text_at, with_alpha(theme::paper, alpha), drawn.text.c_str(), nullptr, wrap); + const auto text = with_alpha(text_colour(*line), alpha); + if (emotes) draw_rich(draw, body, size, text_at, drawn.text, drawn.rich, text, alpha); + else draw->AddText(body, size, text_at, text, drawn.text.c_str(), nullptr, wrap); bottom = top_left.y - gap; } }