/
yaseeeeechka
/
CenterControl
Обзор
Документация
Войти
/
yaseeeeechka
/
CenterControl
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
CenterControl_TCP/include/client/RoomScreen.h
583 строки
21 KB
Yaroslava
New structure of Repo. TCP impl
11 дек 2025, 21:12
11 дек 2025, 21:12
6fdf9d7
Код
Авторство
О чём код?
#ifndef CENTERCONTROL_ROOMSCREEN_H #define CENTERCONTROL_ROOMSCREEN_H #include <SFML/Graphics.hpp> #include <memory> #include <vector> #include <string> #include <algorithm> #include <cctype> #include <cstdint> #include "IScreen.h" #include "Session.h" #include "TextBox.h" // ==================== RoomScreen ====================================== class RoomScreen : public IScreen { public: RoomScreen(IScreenApp *app, const sf::Font &font) : app_(app), btn_back_(sf::Vector2f(150.f, 40.f)), btn_txt_(font, "Back", 20), title_(font, "Room", 24), room_name_txt_(font, "", 18), username_txt_(font, "", 18), players_header_(font, "Players in room", 18), chat_label_(font, "Room chat", 18), chat_send_txt_(font, "Send", 18), quick_btn_hello_(sf::Vector2f(90.f, 36.f)), quick_btn_bye_(sf::Vector2f(90.f, 36.f)), quick_btn_go_(sf::Vector2f(90.f, 36.f)), quick_txt_hello_(font, "Hello!", 16), quick_txt_bye_(font, "Bye!", 16), quick_txt_go_(font, "Let's go!", 16), btn_start_game_txt_(font, "Start game", 18), bgGradient_(sf::PrimitiveType::TriangleStrip, 4) { // ---------- Палитра ---------- btn_primary_color_ = sf::Color(90, 140, 255); btn_primary_hover_color_ = sf::Color(110, 170, 255); btn_secondary_color_ = sf::Color(100, 200, 100); btn_secondary_disabled_ = sf::Color(120, 120, 120); btn_quick_color_ = sf::Color(100, 150, 255); btn_quick_hover_color_ = sf::Color(120, 175, 255); panel_bg_color_ = sf::Color(22, 28, 50, 230); players_row_color_ = sf::Color(33, 41, 70); players_row_hover_color_ = sf::Color(50, 60, 95); // ---------- Фон и градиент ---------- auto winSize = app_->GetWindow().getSize(); float winW = static_cast<float>(winSize.x); float winH = static_cast<float>(winSize.y); bg_overlay_.setSize({winW, winH}); bg_overlay_.setFillColor(sf::Color(15, 20, 35, 230)); bg_overlay_.setPosition({0.f, 0.f}); { sf::Color top(10, 14, 30); sf::Color bottom(10, 18, 50); bgGradient_[0].position = {0.f, 0.f}; bgGradient_[0].color = top; bgGradient_[1].position = {winW, 0.f}; bgGradient_[1].color = top; bgGradient_[2].position = {0.f, winH}; bgGradient_[2].color = bottom; bgGradient_[3].position = {winW, winH}; bgGradient_[3].color = bottom; } // ---------- Кнопка Back (правый верх) ---------- const float margin = 20.f; btn_back_.setPosition({winW - btn_back_.getSize().x - margin, margin}); btn_back_.setFillColor(btn_primary_color_); btn_back_.setOutlineThickness(0.f); btn_txt_.setFillColor(sf::Color::White); CenterTextInRect(btn_txt_, btn_back_); // ---------- Заголовок ---------- title_.setFillColor(sf::Color(235, 240, 255)); { sf::FloatRect tb = title_.getLocalBounds(); float x = (winW - tb.size.x) / 2.f - tb.position.x; float y = margin + 4.f; title_.setPosition({x, y}); } // ---------- Строка с пользователем и именем комнаты ---------- username_txt_.setFillColor(sf::Color(170, 180, 210)); username_txt_.setPosition({20.f, 70.f}); room_name_txt_.setFillColor(sf::Color(210, 215, 240)); room_name_txt_.setPosition({20.f, 100.f}); // ---------- Кнопка Start game (левый верх) ---------- btn_start_game_.setSize({180.f, 40.f}); btn_start_game_.setPosition({20.f, 20.f}); btn_start_game_.setFillColor(btn_secondary_color_); btn_start_game_.setOutlineThickness(0.f); btn_start_game_txt_.setFillColor(sf::Color::White); CenterTextInRect(btn_start_game_txt_, btn_start_game_); // ---------- Панель игроков ---------- players_header_.setFillColor(sf::Color(220, 225, 245)); players_header_.setPosition({20.f, 140.f}); players_list_bg_.setPosition({20.f, players_header_.getPosition().y + 26.f}); players_list_bg_.setSize({360.f, players_item_height_ * 4.f + 12.f}); // до 4 игроков players_list_bg_.setFillColor(panel_bg_color_); players_list_bg_.setOutlineColor(sf::Color(80, 100, 165)); players_list_bg_.setOutlineThickness(1.5f); // ---------- Панель чата ---------- float chatTopY = players_list_bg_.getPosition().y + players_list_bg_.getSize().y + 20.f; chat_label_.setFillColor(sf::Color(220, 225, 245)); chat_label_.setPosition({20.f, chatTopY}); chat_bg_.setPosition({20.f, chatTopY + 26.f}); chat_bg_.setSize({winW - 40.f, 220.f}); chat_bg_.setFillColor(panel_bg_color_); chat_bg_.setOutlineColor(sf::Color(80, 100, 165)); chat_bg_.setOutlineThickness(1.5f); // ---------- Поле ввода + быстрые кнопки + Send ---------- float inputY = chat_bg_.getPosition().y + chat_bg_.getSize().y + 18.f; float inputX = 20.f; float inputWidth = 380.f; chat_input_ = std::make_unique<TextBox>(inputX, inputY, inputWidth, 36.f, font); // Быстрые кнопки float quickY = inputY; quick_btn_hello_.setPosition({inputX + inputWidth + 10.f, quickY}); quick_btn_hello_.setFillColor(btn_quick_color_); quick_txt_hello_.setFillColor(sf::Color::White); CenterTextInRect(quick_txt_hello_, quick_btn_hello_); quick_btn_bye_.setPosition( {quick_btn_hello_.getPosition().x + quick_btn_hello_.getSize().x + 8.f, quickY} ); quick_btn_bye_.setFillColor(btn_quick_color_); quick_txt_bye_.setFillColor(sf::Color::White); CenterTextInRect(quick_txt_bye_, quick_btn_bye_); quick_btn_go_.setPosition( {quick_btn_bye_.getPosition().x + quick_btn_bye_.getSize().x + 8.f, quickY} ); quick_btn_go_.setFillColor(btn_quick_color_); quick_txt_go_.setFillColor(sf::Color::White); CenterTextInRect(quick_txt_go_, quick_btn_go_); // Кнопка Send справа chat_send_btn_.setSize({120.f, 36.f}); chat_send_btn_.setPosition({winW - 20.f - chat_send_btn_.getSize().x, inputY}); chat_send_btn_.setFillColor(btn_primary_color_); chat_send_btn_.setOutlineThickness(0.f); chat_send_txt_.setFillColor(sf::Color::White); CenterTextInRect(chat_send_txt_, chat_send_btn_); // ---------- Коллбэк списка игроков ---------- if (auto sess = app_->GetSession()) { sess->SetRoomPlayersCallback( [this](const std::string &roomName, const std::vector<std::string> &players) { if (roomName == room_name_) { players_ = players; } } ); } } void SetRoomName(const std::string &name) { room_name_ = name; room_name_txt_.setString("Room: " + name); } void HandleEvent(const sf::Event &e) override { if (chat_input_) { chat_input_->HandleEvent(e, app_->GetWindow()); } // Клик мышью if (e.is<sf::Event::MouseButtonPressed>()) { auto m = sf::Mouse::getPosition(app_->GetWindow()); sf::Vector2f mp{(float)m.x, (float)m.y}; // Back if (btn_back_.getGlobalBounds().contains(mp)) { if (auto sess = app_->GetSession()) { if (!room_name_.empty()) sess->LeaveRoom(room_name_); } app_->SwitchScreen("main"); return; } // Start game if (btn_start_game_.getGlobalBounds().contains(mp)) { if (start_enabled_) { if (auto sess = app_->GetSession()) { STATUS st = sess->StartGame(room_name_); if (st == STATUS::RET_SUCCESS) { start_enabled_ = false; btn_start_game_.setFillColor(btn_secondary_disabled_); app_->SwitchScreen("game"); } } } return; } // Send if (chat_send_btn_.getGlobalBounds().contains(mp)) { auto txt = chat_input_->GetText(); Trim(txt); if (!txt.empty() && !room_name_.empty()) { if (auto sess = app_->GetSession()) { sess->SendRoomChatMessage(room_name_, txt); } chat_input_->Clear(); } return; } // Быстрые кнопки if (!room_name_.empty()) { if (quick_btn_hello_.getGlobalBounds().contains(mp)) { if (auto sess = app_->GetSession()) { sess->SendRoomChatMessage(room_name_, "Hello!"); } } else if (quick_btn_bye_.getGlobalBounds().contains(mp)) { if (auto sess = app_->GetSession()) { sess->SendRoomChatMessage(room_name_, "Bye!"); } } else if (quick_btn_go_.getGlobalBounds().contains(mp)) { if (auto sess = app_->GetSession()) { sess->SendRoomChatMessage(room_name_, "Let's go!"); } } } } // Наведение мыши if (auto mv = e.getIf<sf::Event::MouseMoved>()) { sf::Vector2f mp{(float)mv->position.x, (float)mv->position.y}; // hover Back bool overBack = btn_back_.getGlobalBounds().contains(mp); btn_back_.setFillColor(overBack ? btn_primary_hover_color_ : btn_primary_color_); // hover Start game bool overStart = btn_start_game_.getGlobalBounds().contains(mp); if (start_enabled_) { btn_start_game_.setFillColor(overStart ? btn_secondary_color_ : btn_secondary_color_); } else { btn_start_game_.setFillColor(btn_secondary_disabled_); } // hover Send bool overSend = chat_send_btn_.getGlobalBounds().contains(mp); chat_send_btn_.setFillColor(overSend ? btn_primary_hover_color_ : btn_primary_color_); // hover быстрые кнопки bool overHello = quick_btn_hello_.getGlobalBounds().contains(mp); quick_btn_hello_.setFillColor(overHello ? btn_quick_hover_color_ : btn_quick_color_); bool overBye = quick_btn_bye_.getGlobalBounds().contains(mp); quick_btn_bye_.setFillColor(overBye ? btn_quick_hover_color_ : btn_quick_color_); bool overGo = quick_btn_go_.getGlobalBounds().contains(mp); quick_btn_go_.setFillColor(overGo ? btn_quick_hover_color_ : btn_quick_color_); // hover по игрокам hovered_player_index_ = -1; if (players_list_bg_.getGlobalBounds().contains(mp) && !players_.empty()) { float relY = mp.y - (players_list_bg_.getPosition().y + 6.f); if (relY >= 0.f) { int idx = static_cast<int>(relY / players_item_height_); if (idx >= 0 && idx < static_cast<int>(players_.size())) { hovered_player_index_ = idx; } } } } // Esc — назад if (auto key = e.getIf<sf::Event::KeyPressed>()) { if (key->code == sf::Keyboard::Key::Escape) { if (auto sess = app_->GetSession()) { if (!room_name_.empty()) sess->LeaveRoom(room_name_); } app_->SwitchScreen("main"); return; } } } void OnShow() override { auto sess = app_->GetSession(); if (sess) { username_txt_.setString("User: " + sess->GetCurrentUser()); room_name_ = sess->GetCurrentRoom(); room_name_txt_.setString("Room: " + room_name_); sess->SetChatCallback([this](const std::vector<ChatMessageDTO> &msgs) { chat_messages_ = msgs; }); } else { username_txt_.setString(""); room_name_.clear(); room_name_txt_.setString("Room: "); } players_.clear(); chat_messages_.clear(); pollAccum_ = 0.f; pollClock_.restart(); start_enabled_ = true; btn_start_game_.setFillColor(btn_secondary_color_); gamePollAccum_ = 0.f; if (sess && !room_name_.empty()) { sess->RequestRoomPlayers(room_name_); sess->RequestRoomChatHistory(room_name_); auto gs = sess->QueryGameState(room_name_); if (gs.status == STATUS::RET_ERROR) { // Игровой сессии нет — можно стартовать sess->SetSuppressAutoGame(false); start_enabled_ = players_.size() >= 2; btn_start_game_.setFillColor( start_enabled_ ? btn_secondary_color_ : btn_secondary_disabled_ ); } else { // игра уже есть в комнате start_enabled_ = false; btn_start_game_.setFillColor(btn_secondary_disabled_); if (!sess->GetSuppressAutoGame() && (gs.status == STATUS::RET_SUCCESS || gs.status == STATUS::RET_ALREADY_LOGGED_IN)) { app_->SwitchScreen("game"); } } } } void OnHide() override { // по умолчанию не выходим из комнаты } void Update(float dt) override { // периодический запрос игроков/чата pollAccum_ += dt; if (pollAccum_ >= pollIntervalSec_) { pollAccum_ = 0.f; if (auto sess = app_->GetSession()) { if (!room_name_.empty()) { sess->RequestRoomPlayers(room_name_); sess->RequestRoomChatHistory(room_name_); } } } // периодический опрос состояния игры gamePollAccum_ += dt; if (gamePollAccum_ >= gamePollIntervalSec_) { gamePollAccum_ = 0.f; if (auto sess = app_->GetSession()) { if (!room_name_.empty()) { auto gs = sess->QueryGameState(room_name_); if (gs.status == STATUS::RET_ERROR) { // Игровой сессии нет — можно стартовать sess->SetSuppressAutoGame(false); bool canStart = (players_.size() >= 2); // например, >=2 игроков start_enabled_ = canStart; btn_start_game_.setFillColor( canStart ? btn_secondary_color_ : btn_secondary_disabled_ ); } else { // игра есть start_enabled_ = false; btn_start_game_.setFillColor(btn_secondary_disabled_); if (!sess->GetSuppressAutoGame() && (gs.status == STATUS::RET_SUCCESS || gs.status == STATUS::RET_ALREADY_LOGGED_IN)) { app_->SwitchScreen("game"); } } } } } } void Render(sf::RenderWindow &w) override { // фон w.draw(bgGradient_); w.draw(bg_overlay_); // шапка w.draw(btn_start_game_); w.draw(btn_start_game_txt_); w.draw(btn_back_); w.draw(btn_txt_); w.draw(title_); // пользователь и комната w.draw(username_txt_); w.draw(room_name_txt_); // игроки w.draw(players_header_); w.draw(players_list_bg_); float baseX = players_list_bg_.getPosition().x + 6.f; float baseY = players_list_bg_.getPosition().y + 6.f; for (std::size_t i = 0; i < players_.size() && i < 4; ++i) { sf::RectangleShape rowRect( sf::Vector2f(players_list_bg_.getSize().x - 12.f, players_item_height_ - 4.f) ); rowRect.setPosition({baseX, baseY + static_cast<float>(i) * players_item_height_}); sf::Color fillColor = players_row_color_; if (static_cast<int>(i) == hovered_player_index_) { fillColor = players_row_hover_color_; } rowRect.setFillColor(fillColor); rowRect.setOutlineColor(sf::Color(90, 110, 185)); rowRect.setOutlineThickness( static_cast<int>(i) == hovered_player_index_ ? 2.f : 1.f ); w.draw(rowRect); std::string line = std::to_string(static_cast<int>(i) + 1) + ". " + players_[i]; sf::Text t(title_.getFont(), line, 18); t.setFillColor(sf::Color(235, 240, 255)); t.setPosition({rowRect.getPosition().x + 10.f, rowRect.getPosition().y + 4.f}); w.draw(t); } // чат w.draw(chat_label_); w.draw(chat_bg_); int maxLines = 10; int total = static_cast<int>(chat_messages_.size()); int start = std::max(0, total - maxLines); float cx = chat_bg_.getPosition().x + 8.f; float cy = chat_bg_.getPosition().y + 8.f; for (int i = start; i < total; ++i) { const auto &m = chat_messages_[i]; std::string line = m.user + ": " + m.text; sf::Text t(title_.getFont(), line, 16); t.setFillColor(sf::Color(225, 230, 245)); t.setPosition({cx, cy}); w.draw(t); cy += 18.f; } if (chat_input_) chat_input_->Draw(w); // быстрые кнопки w.draw(quick_btn_hello_); w.draw(quick_txt_hello_); w.draw(quick_btn_bye_); w.draw(quick_txt_bye_); w.draw(quick_btn_go_); w.draw(quick_txt_go_); // Send w.draw(chat_send_btn_); w.draw(chat_send_txt_); } private: static void Trim(std::string &s) { s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) { return !std::isspace(ch); })); s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) { return !std::isspace(ch); }).base(), s.end()); } void CenterTextInRect(sf::Text &txt, const sf::RectangleShape &rect) { sf::FloatRect bounds = txt.getLocalBounds(); sf::Vector2f rectPos = rect.getPosition(); sf::Vector2f rectSize = rect.getSize(); sf::Vector2f textPos; // SFML 3: bounds.position / bounds.size textPos.x = rectPos.x + (rectSize.x - bounds.size.x) / 2.f - bounds.position.x; textPos.y = rectPos.y + (rectSize.y - bounds.size.y) / 2.f - bounds.position.y; txt.setPosition(textPos); } private: IScreenApp *app_; // фон sf::VertexArray bgGradient_; sf::RectangleShape bg_overlay_; // шапка sf::RectangleShape btn_back_; sf::Text btn_txt_; sf::Text title_; sf::RectangleShape btn_start_game_; sf::Text btn_start_game_txt_; // пользователь / комната sf::Text username_txt_; sf::Text room_name_txt_; std::string room_name_; // игроки sf::Text players_header_; std::vector<std::string> players_; sf::RectangleShape players_list_bg_; const float players_item_height_ = 32.f; int hovered_player_index_ = -1; // чат sf::Text chat_label_; sf::RectangleShape chat_bg_; std::unique_ptr<TextBox> chat_input_; sf::RectangleShape chat_send_btn_; sf::Text chat_send_txt_; std::vector<ChatMessageDTO> chat_messages_; // быстрые сообщения sf::RectangleShape quick_btn_hello_; sf::RectangleShape quick_btn_bye_; sf::RectangleShape quick_btn_go_; sf::Text quick_txt_hello_; sf::Text quick_txt_bye_; sf::Text quick_txt_go_; // таймеры sf::Clock pollClock_; float pollAccum_ = 0.f; const float pollIntervalSec_ = 1.0f; float gamePollAccum_ = 0.f; const float gamePollIntervalSec_ = 0.3f; // состояние кнопки Start bool start_enabled_ = true; // цвета sf::Color btn_primary_color_; sf::Color btn_primary_hover_color_; sf::Color btn_secondary_color_; sf::Color btn_secondary_disabled_; sf::Color btn_quick_color_; sf::Color btn_quick_hover_color_; sf::Color panel_bg_color_; sf::Color players_row_color_; sf::Color players_row_hover_color_; }; #endif // CENTERCONTROL_ROOMSCREEN_H