mirror of
https://github.com/vcmi/vcmi.git
synced 2024-12-24 22:14:36 +02:00
UI improvements for lobby:
- Added notifications sounds for invites and chat messages - Added notifications for unread chat messages in inactive channels - Added click sound when switching between channels - Added workaround to prevent clicks due to list recreation - Partial support for receiving invites
This commit is contained in:
parent
536156dd92
commit
69f7b3169e
@ -95,7 +95,8 @@
|
||||
"vcmi.lobby.room.description.new" : "To start the game, select a scenario or set up a random map.",
|
||||
"vcmi.lobby.room.description.load" : "To start the game, use one of your saved games.",
|
||||
"vcmi.lobby.room.description.limit" : "Up to %d players can enter your room, including you.",
|
||||
"vcmi.lobby.room.invite" : "Invite Players",
|
||||
"vcmi.lobby.invite.header" : "Invite Players",
|
||||
"vcmi.lobby.invite.notification" : "Player has invited you to their game room. You can now join their private room.",
|
||||
"vcmi.lobby.room.new" : "New Game",
|
||||
"vcmi.lobby.room.load" : "Load Game",
|
||||
"vcmi.lobby.room.type" : "Room Type",
|
||||
@ -103,6 +104,7 @@
|
||||
"vcmi.lobby.room.state.public" : "Public",
|
||||
"vcmi.lobby.room.state.private" : "Private",
|
||||
"vcmi.lobby.room.state.busy" : "In Game",
|
||||
"vcmi.lobby.room.state.invited" : "Invited",
|
||||
|
||||
"vcmi.client.errors.invalidMap" : "{Invalid map or campaign}\n\nFailed to start game! Selected map or campaign might be invalid or corrupted. Reason:\n%s",
|
||||
"vcmi.client.errors.missingCampaigns" : "{Missing data files}\n\nCampaigns data files were not found! You may be using incomplete or corrupted Heroes 3 data files. Please reinstall game data.",
|
||||
|
@ -15,12 +15,13 @@
|
||||
#include "GlobalLobbyLoginWindow.h"
|
||||
#include "GlobalLobbyWindow.h"
|
||||
|
||||
#include "../CGameInfo.h"
|
||||
#include "../CMusicHandler.h"
|
||||
#include "../CServerHandler.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/WindowHandler.h"
|
||||
#include "../windows/InfoWindows.h"
|
||||
#include "../CServerHandler.h"
|
||||
#include "../mainmenu/CMainMenu.h"
|
||||
#include "../CGameInfo.h"
|
||||
#include "../windows/InfoWindows.h"
|
||||
|
||||
#include "../../lib/CConfigHandler.h"
|
||||
#include "../../lib/MetaString.h"
|
||||
@ -189,6 +190,8 @@ void GlobalLobbyClient::receiveChatMessage(const JsonNode & json)
|
||||
auto lobbyWindowPtr = lobbyWindow.lock();
|
||||
if(lobbyWindowPtr)
|
||||
lobbyWindowPtr->onGameChatMessage(message.displayName, message.messageText, message.timeFormatted, channelType, channelName);
|
||||
|
||||
CCS->soundh->playSound(AudioPath::builtin("CHAT"));
|
||||
}
|
||||
|
||||
void GlobalLobbyClient::receiveActiveAccounts(const JsonNode & json)
|
||||
@ -280,10 +283,18 @@ void GlobalLobbyClient::receiveMatchesHistory(const JsonNode & json)
|
||||
void GlobalLobbyClient::receiveInviteReceived(const JsonNode & json)
|
||||
{
|
||||
auto lobbyWindowPtr = lobbyWindow.lock();
|
||||
std::string gameRoomID = json["gameRoomID"].String();
|
||||
std::string accountID = json["accountID"].String();
|
||||
if(lobbyWindowPtr)
|
||||
lobbyWindowPtr->onMatchesHistory(activeRooms);
|
||||
{
|
||||
std::string message = MetaString::createFromTextID("vcmi.lobby.invite.notification").toString();
|
||||
std::string time = getCurrentTimeFormatted();
|
||||
|
||||
assert(0); //TODO
|
||||
lobbyWindowPtr->onGameChatMessage("System", message, time, "player", accountID);
|
||||
lobbyWindowPtr->onInviteReceived(gameRoomID, accountID);
|
||||
}
|
||||
|
||||
CCS->soundh->playSound(AudioPath::builtin("CHAT"));
|
||||
}
|
||||
|
||||
void GlobalLobbyClient::receiveJoinRoomSuccess(const JsonNode & json)
|
||||
|
@ -57,7 +57,7 @@ GlobalLobbyInviteWindow::GlobalLobbyInviteWindow()
|
||||
filledBackground = std::make_shared<FilledTexturePlayerColored>(ImagePath::builtin("DiBoxBck"), Rect(0, 0, pos.w, pos.h));
|
||||
filledBackground->playerColored(PlayerColor(1));
|
||||
labelTitle = std::make_shared<CLabel>(
|
||||
pos.w / 2, 20, FONT_BIG, ETextAlignment::CENTER, Colors::YELLOW, MetaString::createFromTextID("vcmi.lobby.room.invite").toString()
|
||||
pos.w / 2, 20, FONT_BIG, ETextAlignment::CENTER, Colors::YELLOW, MetaString::createFromTextID("vcmi.lobby.invite.header").toString()
|
||||
);
|
||||
|
||||
const auto & createAccountCardCallback = [this](size_t index) -> std::shared_ptr<CIntObject>
|
||||
|
@ -14,6 +14,8 @@
|
||||
#include "GlobalLobbyClient.h"
|
||||
#include "GlobalLobbyWindow.h"
|
||||
|
||||
#include "../CGameInfo.h"
|
||||
#include "../CMusicHandler.h"
|
||||
#include "../CServerHandler.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/WindowHandler.h"
|
||||
@ -186,18 +188,22 @@ GlobalLobbyChannelCardBase::GlobalLobbyChannelCardBase(GlobalLobbyWindow * windo
|
||||
|
||||
if (window->isChannelOpen(channelType, channelName))
|
||||
backgroundOverlay = std::make_shared<TransparentFilledRectangle>(Rect(0, 0, pos.w, pos.h), ColorRGBA(0, 0, 0, 128), Colors::YELLOW, 2);
|
||||
else if (window->isChannelUnread(channelType, channelName))
|
||||
backgroundOverlay = std::make_shared<TransparentFilledRectangle>(Rect(0, 0, pos.w, pos.h), ColorRGBA(0, 0, 0, 128), Colors::WHITE, 1);
|
||||
else
|
||||
backgroundOverlay = std::make_shared<TransparentFilledRectangle>(Rect(0, 0, pos.w, pos.h), ColorRGBA(0, 0, 0, 128), ColorRGBA(64, 64, 64, 64), 1);
|
||||
}
|
||||
|
||||
void GlobalLobbyChannelCardBase::clickPressed(const Point & cursorPosition)
|
||||
{
|
||||
CCS->soundh->playSound(soundBase::button);
|
||||
window->doOpenChannel(channelType, channelName, channelDescription);
|
||||
}
|
||||
|
||||
GlobalLobbyAccountCard::GlobalLobbyAccountCard(GlobalLobbyWindow * window, const GlobalLobbyAccount & accountDescription)
|
||||
: GlobalLobbyChannelCardBase(window, Point(130, 40), "player", accountDescription.accountID, accountDescription.displayName)
|
||||
{
|
||||
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
|
||||
labelName = std::make_shared<CLabel>(5, 10, FONT_SMALL, ETextAlignment::CENTERLEFT, Colors::WHITE, accountDescription.displayName);
|
||||
labelStatus = std::make_shared<CLabel>(5, 30, FONT_SMALL, ETextAlignment::CENTERLEFT, Colors::YELLOW, accountDescription.status);
|
||||
}
|
||||
|
@ -49,6 +49,7 @@ void GlobalLobbyWindow::doOpenChannel(const std::string & channelType, const std
|
||||
currentChannelType = channelType;
|
||||
currentChannelName = channelName;
|
||||
chatHistory.clear();
|
||||
unreadChannels.erase(channelType + "_" + channelName);
|
||||
widget->getGameChat()->setText("");
|
||||
|
||||
auto history = CSH->getGlobalLobby().getChannelHistory(channelType, channelName);
|
||||
@ -110,7 +111,14 @@ void GlobalLobbyWindow::doJoinRoom(const std::string & roomID)
|
||||
void GlobalLobbyWindow::onGameChatMessage(const std::string & sender, const std::string & message, const std::string & when, const std::string & channelType, const std::string & channelName)
|
||||
{
|
||||
if (channelType != currentChannelType || channelName != currentChannelName)
|
||||
return; // TODO: send ping to player that another channel got a new message
|
||||
{
|
||||
// mark channel as unread
|
||||
unreadChannels.insert(channelType + "_" + channelName);
|
||||
widget->getAccountList()->reset();
|
||||
widget->getChannelList()->reset();
|
||||
widget->getMatchList()->reset();
|
||||
return;
|
||||
}
|
||||
|
||||
MetaString chatMessageFormatted;
|
||||
chatMessageFormatted.appendRawString("[%s] {%s}: %s\n");
|
||||
@ -123,6 +131,11 @@ void GlobalLobbyWindow::onGameChatMessage(const std::string & sender, const std:
|
||||
widget->getGameChat()->setText(chatHistory);
|
||||
}
|
||||
|
||||
bool GlobalLobbyWindow::isChannelUnread(const std::string & channelType, const std::string & channelName)
|
||||
{
|
||||
return unreadChannels.count(channelType + "_" + channelName) > 0;
|
||||
}
|
||||
|
||||
void GlobalLobbyWindow::onActiveAccounts(const std::vector<GlobalLobbyAccount> & accounts)
|
||||
{
|
||||
if (accounts.size() == widget->getAccountList()->size())
|
||||
@ -159,6 +172,11 @@ void GlobalLobbyWindow::onMatchesHistory(const std::vector<GlobalLobbyRoom> & hi
|
||||
widget->getMatchListHeader()->setText(text.toString());
|
||||
}
|
||||
|
||||
void GlobalLobbyWindow::onInviteReceived(const std::string & invitedRoomID, const std::string & invitedByAccountID)
|
||||
{
|
||||
widget->getRoomList()->reset();
|
||||
}
|
||||
|
||||
void GlobalLobbyWindow::onJoinedRoom()
|
||||
{
|
||||
widget->getAccountList()->reset();
|
||||
|
@ -22,6 +22,7 @@ class GlobalLobbyWindow : public CWindowObject
|
||||
std::string currentChannelName;
|
||||
|
||||
std::shared_ptr<GlobalLobbyWidget> widget;
|
||||
std::set<std::string> unreadChannels;
|
||||
|
||||
public:
|
||||
GlobalLobbyWindow();
|
||||
@ -36,6 +37,7 @@ public:
|
||||
|
||||
/// Returns true if provided chat channel is the one that is currently open in UI
|
||||
bool isChannelOpen(const std::string & channelType, const std::string & channelName);
|
||||
bool isChannelUnread(const std::string & channelType, const std::string & channelName);
|
||||
|
||||
// Callbacks for network packs
|
||||
|
||||
@ -43,6 +45,7 @@ public:
|
||||
void onActiveAccounts(const std::vector<GlobalLobbyAccount> & accounts);
|
||||
void onActiveRooms(const std::vector<GlobalLobbyRoom> & rooms);
|
||||
void onMatchesHistory(const std::vector<GlobalLobbyRoom> & history);
|
||||
void onInviteReceived(const std::string & invitedRoomID, const std::string & invitedByAccountID);
|
||||
void onJoinedRoom();
|
||||
void onLeftRoom();
|
||||
};
|
||||
|
@ -66,6 +66,7 @@ size_t CTabbedInt::getActive() const
|
||||
|
||||
void CTabbedInt::reset()
|
||||
{
|
||||
|
||||
deleteItem(activeTab);
|
||||
activeTab = createItem(activeID);
|
||||
activeTab->moveTo(pos.topLeft());
|
||||
@ -127,6 +128,11 @@ void CListBox::updatePositions()
|
||||
|
||||
void CListBox::reset()
|
||||
{
|
||||
// hack to ensure that all items will be recreated with new address
|
||||
// save current item list so all shared_ptr's will be destroyed only on scope exit and not inside loop below
|
||||
// see comment in EventDispatcher::handleLeftButtonClick for details on why this hack is needed
|
||||
auto itemsCopy = items;
|
||||
|
||||
size_t current = first;
|
||||
for (auto & elem : items)
|
||||
{
|
||||
@ -279,4 +285,4 @@ void CListBoxWithCallback::moveToPrev()
|
||||
CListBox::moveToPrev();
|
||||
if(movedPosCallback)
|
||||
movedPosCallback(getPos());
|
||||
}
|
||||
}
|
||||
|
@ -198,6 +198,12 @@ void LobbyDatabase::prepareStatements()
|
||||
WHERE roomID = ?
|
||||
)");
|
||||
|
||||
getAccountInviteStatusStatement = database->prepare(R"(
|
||||
SELECT COUNT(accountID)
|
||||
FROM gameRoomInvites
|
||||
WHERE accountID = ? AND roomID = ?
|
||||
)");
|
||||
|
||||
getAccountGameHistoryStatement = database->prepare(R"(
|
||||
SELECT gr.roomID, hostAccountID, displayName, description, status, playerLimit, strftime('%s',CURRENT_TIMESTAMP)- strftime('%s',gr.creationTime) AS secondsElapsed
|
||||
FROM gameRoomPlayers grp
|
||||
@ -438,8 +444,17 @@ LobbyCookieStatus LobbyDatabase::getAccountCookieStatus(const std::string & acco
|
||||
|
||||
LobbyInviteStatus LobbyDatabase::getAccountInviteStatus(const std::string & accountID, const std::string & roomID)
|
||||
{
|
||||
assert(0);
|
||||
return {};
|
||||
int result = 0;
|
||||
|
||||
getAccountInviteStatusStatement->setBinds(accountID, roomID);
|
||||
if(getAccountInviteStatusStatement->execute())
|
||||
getAccountInviteStatusStatement->getColumns(result);
|
||||
getAccountInviteStatusStatement->reset();
|
||||
|
||||
if (result > 0)
|
||||
return LobbyInviteStatus::INVITED;
|
||||
else
|
||||
return LobbyInviteStatus::NOT_INVITED;
|
||||
}
|
||||
|
||||
LobbyRoomState LobbyDatabase::getGameRoomStatus(const std::string & roomID)
|
||||
|
@ -44,6 +44,7 @@ class LobbyDatabase
|
||||
SQLiteStatementPtr getAccountGameHistoryStatement;
|
||||
SQLiteStatementPtr getActiveGameRoomsStatement;
|
||||
SQLiteStatementPtr getActiveAccountsStatement;
|
||||
SQLiteStatementPtr getAccountInviteStatusStatement;
|
||||
SQLiteStatementPtr getAccountGameRoomStatement;
|
||||
SQLiteStatementPtr getAccountDisplayNameStatement;
|
||||
SQLiteStatementPtr countRoomUsedSlotsStatement;
|
||||
|
@ -761,10 +761,10 @@ void LobbyServer::receiveSendInvite(const NetworkConnectionPtr & connection, con
|
||||
std::string accountID = json["accountID"].String();
|
||||
std::string gameRoomID = database->getAccountGameRoom(senderName);
|
||||
|
||||
auto targetAccount = findAccount(accountID);
|
||||
auto targetAccountConnection = findAccount(accountID);
|
||||
|
||||
if(!targetAccount)
|
||||
return sendOperationFailed(connection, "Invalid account to invite!");
|
||||
if(!targetAccountConnection)
|
||||
return sendOperationFailed(connection, "Player is offline or does not exists!");
|
||||
|
||||
if(!database->isPlayerInGameRoom(senderName))
|
||||
return sendOperationFailed(connection, "You are not in the room!");
|
||||
@ -776,7 +776,7 @@ void LobbyServer::receiveSendInvite(const NetworkConnectionPtr & connection, con
|
||||
return sendOperationFailed(connection, "This player is already invited!");
|
||||
|
||||
database->insertGameRoomInvite(accountID, gameRoomID);
|
||||
sendInviteReceived(targetAccount, senderName, gameRoomID);
|
||||
sendInviteReceived(targetAccountConnection, senderName, gameRoomID);
|
||||
}
|
||||
|
||||
LobbyServer::~LobbyServer() = default;
|
||||
|
Loading…
Reference in New Issue
Block a user