Browse Source

Added UI for inviting players into a match

Ivan Savenko 1 year ago
parent
commit
ace43e97b9

+ 3 - 2
Mods/vcmi/config/vcmi/english.json

@@ -82,10 +82,10 @@
 	"vcmi.lobby.header.channels" : "Chat Channels",
 	"vcmi.lobby.header.chat.global" : "Global Game Chat - %s", // %s -> language name
 	"vcmi.lobby.header.chat.match" : "Previous game chat from %s", // %s -> game start date & time
-	"vcmi.lobby.header.chat.private" : "Private chat with %s", // %s -> nickname of another player
+	"vcmi.lobby.header.chat.player" : "Private chat with %s", // %s -> nickname of another player
 	"vcmi.lobby.header.history" : "Your Previous Games",
 	"vcmi.lobby.header.players" : "Players Online - %d",
-	"vcmi.lobby.match.solo" : "Solo Game",
+	"vcmi.lobby.match.solo" : "Singleplayer Game",
 	"vcmi.lobby.match.duel" : "Game with %s",
 	"vcmi.lobby.match.multi" : "%d players",
 	"vcmi.lobby.room.create" : "Create New Room",
@@ -95,6 +95,7 @@
 	"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.room.new" : "New Game",
 	"vcmi.lobby.room.load" : "Load Game",
 	"vcmi.lobby.room.type" : "Room Type",

+ 1 - 1
Mods/vcmi/config/vcmi/ukrainian.json

@@ -82,7 +82,7 @@
 	"vcmi.lobby.header.channels" : "Канали чату",
 	"vcmi.lobby.header.chat.global" : "Глобальний ігровий чат - %s", // %s -> language name
 	"vcmi.lobby.header.chat.match" : "Чат минулої гри від %s", // %s -> game start date & time
-	"vcmi.lobby.header.chat.private" : "Приватний чат з %s", // %s -> nickname of another player
+	"vcmi.lobby.header.chat.player" : "Приватний чат з %s", // %s -> nickname of another player
 	"vcmi.lobby.header.history" : "Ваші попередні ігри",
 	"vcmi.lobby.header.players" : "Гравці в мережі - %d",
 	"vcmi.lobby.match.solo" : "Одиночна гра",

+ 2 - 0
client/CMakeLists.txt

@@ -96,6 +96,7 @@ set(client_SRCS
 	renderSDL/SDL_Extensions.cpp
 
 	globalLobby/GlobalLobbyClient.cpp
+	globalLobby/GlobalLobbyInviteWindow.cpp
 	globalLobby/GlobalLobbyLoginWindow.cpp
 	globalLobby/GlobalLobbyServerSetup.cpp
 	globalLobby/GlobalLobbyWidget.cpp
@@ -281,6 +282,7 @@ set(client_HEADERS
 
 	globalLobby/GlobalLobbyClient.h
 	globalLobby/GlobalLobbyDefines.h
+	globalLobby/GlobalLobbyInviteWindow.h
 	globalLobby/GlobalLobbyLoginWindow.h
 	globalLobby/GlobalLobbyServerSetup.h
 	globalLobby/GlobalLobbyWidget.h

+ 16 - 6
client/globalLobby/GlobalLobbyClient.cpp

@@ -11,6 +11,7 @@
 #include "StdInc.h"
 #include "GlobalLobbyClient.h"
 
+#include "GlobalLobbyInviteWindow.h"
 #include "GlobalLobbyLoginWindow.h"
 #include "GlobalLobbyWindow.h"
 
@@ -194,7 +195,7 @@ void GlobalLobbyClient::receiveActiveAccounts(const JsonNode & json)
 {
 	activeAccounts.clear();
 
-	for (auto const & jsonEntry : json["accounts"].Vector())
+	for(const auto & jsonEntry : json["accounts"].Vector())
 	{
 		GlobalLobbyAccount account;
 
@@ -214,7 +215,7 @@ void GlobalLobbyClient::receiveActiveGameRooms(const JsonNode & json)
 {
 	activeRooms.clear();
 
-	for (auto const & jsonEntry : json["gameRooms"].Vector())
+	for(const auto & jsonEntry : json["gameRooms"].Vector())
 	{
 		GlobalLobbyRoom room;
 
@@ -226,7 +227,7 @@ void GlobalLobbyClient::receiveActiveGameRooms(const JsonNode & json)
 		int ageSeconds = jsonEntry["ageSeconds"].Integer();
 		room.startDateFormatted = getCurrentDateTimeFormatted(-ageSeconds);
 
-		for (auto const & jsonParticipant : jsonEntry["participants"].Vector())
+		for(const auto & jsonParticipant : jsonEntry["participants"].Vector())
 		{
 			GlobalLobbyAccount account;
 			account.accountID =  jsonParticipant["accountID"].String();
@@ -247,7 +248,7 @@ void GlobalLobbyClient::receiveMatchesHistory(const JsonNode & json)
 {
 	matchesHistory.clear();
 
-	for (auto const & jsonEntry : json["matchesHistory"].Vector())
+	for(const auto & jsonEntry : json["matchesHistory"].Vector())
 	{
 		GlobalLobbyRoom room;
 
@@ -259,7 +260,7 @@ void GlobalLobbyClient::receiveMatchesHistory(const JsonNode & json)
 		int ageSeconds = jsonEntry["ageSeconds"].Integer();
 		room.startDateFormatted = getCurrentDateTimeFormatted(-ageSeconds);
 
-		for (auto const & jsonParticipant : jsonEntry["participants"].Vector())
+		for(const auto & jsonParticipant : jsonEntry["participants"].Vector())
 		{
 			GlobalLobbyAccount account;
 			account.accountID =  jsonParticipant["accountID"].String();
@@ -273,11 +274,15 @@ void GlobalLobbyClient::receiveMatchesHistory(const JsonNode & json)
 
 	auto lobbyWindowPtr = lobbyWindow.lock();
 	if(lobbyWindowPtr)
-		lobbyWindowPtr->onMatchesHistory(activeRooms);
+		lobbyWindowPtr->onMatchesHistory(matchesHistory);
 }
 
 void GlobalLobbyClient::receiveInviteReceived(const JsonNode & json)
 {
+	auto lobbyWindowPtr = lobbyWindow.lock();
+	if(lobbyWindowPtr)
+		lobbyWindowPtr->onMatchesHistory(activeRooms);
+
 	assert(0); //TODO
 }
 
@@ -473,6 +478,11 @@ void GlobalLobbyClient::activateInterface()
 		GH.windows().pushWindow(createLoginWindow());
 }
 
+void GlobalLobbyClient::activateRoomInviteInterface()
+{
+	GH.windows().createAndPushWindow<GlobalLobbyInviteWindow>();
+}
+
 void GlobalLobbyClient::sendProxyConnectionLogin(const NetworkConnectionPtr & netConnection)
 {
 	JsonNode toSend;

+ 1 - 0
client/globalLobby/GlobalLobbyClient.h

@@ -69,6 +69,7 @@ public:
 
 	/// Activate interface and pushes lobby UI as top window
 	void activateInterface();
+	void activateRoomInviteInterface();
 
 	void sendMatchChatMessage(const std::string & messageText);
 	void sendMessage(const JsonNode & data);

+ 78 - 0
client/globalLobby/GlobalLobbyInviteWindow.cpp

@@ -0,0 +1,78 @@
+/*
+ * GlobalLobbyInviteWindow.cpp, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+
+#include "StdInc.h"
+#include "GlobalLobbyInviteWindow.h"
+
+#include "GlobalLobbyClient.h"
+
+#include "../CServerHandler.h"
+#include "../gui/CGuiHandler.h"
+#include "../widgets/Buttons.h"
+#include "../widgets/GraphicalPrimitiveCanvas.h"
+#include "../widgets/Images.h"
+#include "../widgets/ObjectLists.h"
+#include "../widgets/TextControls.h"
+
+#include "../../lib/MetaString.h"
+#include "../../lib/json/JsonNode.h"
+
+GlobalLobbyInviteAccountCard::GlobalLobbyInviteAccountCard(GlobalLobbyInviteWindow * window, const GlobalLobbyAccount & accountDescription)
+	: accountID(accountDescription.accountID)
+{
+	pos.w = 200;
+	pos.h = 40;
+	addUsedEvents(LCLICK);
+
+	OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
+	backgroundOverlay = std::make_shared<TransparentFilledRectangle>(Rect(0, 0, pos.w, pos.h), ColorRGBA(0, 0, 0, 128), ColorRGBA(64, 64, 64, 64), 1);
+	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);
+}
+
+void GlobalLobbyInviteAccountCard::clickPressed(const Point & cursorPosition)
+{
+	JsonNode message;
+	message["type"].String() = "sendInvite";
+	message["accountID"].String() = accountID;
+
+	CSH->getGlobalLobby().sendMessage(message);
+}
+
+GlobalLobbyInviteWindow::GlobalLobbyInviteWindow()
+	: CWindowObject(BORDERED)
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
+
+	pos.w = 236;
+	pos.h = 400;
+
+	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()
+	);
+
+	const auto & createAccountCardCallback = [this](size_t index) -> std::shared_ptr<CIntObject>
+	{
+		const auto & accounts = CSH->getGlobalLobby().getActiveAccounts();
+
+		if(index < accounts.size())
+			return std::make_shared<GlobalLobbyInviteAccountCard>(this, accounts[index]);
+		return std::make_shared<CIntObject>();
+	};
+
+	listBackground = std::make_shared<TransparentFilledRectangle>(Rect(8, 48, 220, 304), ColorRGBA(0, 0, 0, 64), ColorRGBA(64, 80, 128, 255), 1);
+	accountList = std::make_shared<CListBox>(createAccountCardCallback, Point(10, 50), Point(0, 40), 8, 0, 0, 1 | 4, Rect(200, 0, 300, 300));
+
+	buttonClose = std::make_shared<CButton>(Point(86, 364), AnimationPath::builtin("MuBchck"), CButton::tooltip(), [this]() { close(); } );
+
+	center();
+}

+ 46 - 0
client/globalLobby/GlobalLobbyInviteWindow.h

@@ -0,0 +1,46 @@
+/*
+ * GlobalLobbyInviteWindow.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+#pragma once
+
+#include "../windows/CWindowObject.h"
+
+class CLabel;
+class FilledTexturePlayerColored;
+class TransparentFilledRectangle;
+class CListBox;
+class CButton;
+struct GlobalLobbyAccount;
+
+class GlobalLobbyInviteWindow : public CWindowObject
+{
+	std::shared_ptr<FilledTexturePlayerColored> filledBackground;
+	std::shared_ptr<CLabel> labelTitle;
+	std::shared_ptr<CListBox> accountList;
+	std::shared_ptr<TransparentFilledRectangle> listBackground;
+	std::shared_ptr<CButton> buttonClose;
+
+public:
+	GlobalLobbyInviteWindow();
+};
+
+class GlobalLobbyInviteAccountCard : public CIntObject
+{
+	std::string accountID;
+
+	std::shared_ptr<TransparentFilledRectangle> backgroundOverlay;
+	std::shared_ptr<CLabel> labelName;
+	std::shared_ptr<CLabel> labelStatus;
+	std::shared_ptr<CLabel> labelInviteStatus;
+	std::shared_ptr<CButton> buttonInvite;
+
+	void clickPressed(const Point & cursorPosition) override;
+public:
+	GlobalLobbyInviteAccountCard(GlobalLobbyInviteWindow * window, const GlobalLobbyAccount & accountDescription);
+};

+ 4 - 1
client/globalLobby/GlobalLobbyWidget.cpp

@@ -106,7 +106,10 @@ std::shared_ptr<CIntObject> GlobalLobbyWidget::buildItemList(const JsonNode & co
 	int sliderMode = config["sliderSize"].isNull() ? 0 : (1 | 4); //  present, vertical, blue
 	int initialPos = 0;
 
-	return std::make_shared<CListBox>(callback, position, itemOffset, visibleAmount, totalAmount, initialPos, sliderMode, Rect(sliderPosition, sliderSize));
+	auto result = std::make_shared<CListBox>(callback, position, itemOffset, visibleAmount, totalAmount, initialPos, sliderMode, Rect(sliderPosition, sliderSize));
+
+	result->setRedrawParent(true);
+	return result;
 }
 
 std::shared_ptr<CLabel> GlobalLobbyWidget::getAccountNameLabel()

+ 0 - 1
client/globalLobby/GlobalLobbyWidget.h

@@ -62,7 +62,6 @@ class GlobalLobbyAccountCard : public GlobalLobbyChannelCardBase
 	std::shared_ptr<TransparentFilledRectangle> backgroundOverlay;
 	std::shared_ptr<CLabel> labelName;
 	std::shared_ptr<CLabel> labelStatus;
-	std::shared_ptr<CButton> buttonInvite;
 
 public:
 	GlobalLobbyAccountCard(GlobalLobbyWindow * window, const GlobalLobbyAccount & accountDescription);

+ 25 - 23
client/lobby/CSelectionBase.cpp

@@ -26,6 +26,7 @@
 #include "../gui/CGuiHandler.h"
 #include "../gui/Shortcut.h"
 #include "../gui/WindowHandler.h"
+#include "../globalLobby/GlobalLobbyClient.h"
 #include "../mainmenu/CMainMenu.h"
 #include "../widgets/Buttons.h"
 #include "../widgets/CComponent.h"
@@ -137,6 +138,9 @@ InfoCard::InfoCard()
 	playerListBg = std::make_shared<CPicture>(ImagePath::builtin("CHATPLUG.bmp"), 16, 276);
 	chat = std::make_shared<CChatBox>(Rect(18, 126, 335, 143));
 
+	buttonInvitePlayers = std::make_shared<CButton>(Point(30, 360), AnimationPath::builtin("GSPBUT2.DEF"), CGI->generaltexth->zelp[105], [=](){ CSH->getGlobalLobby().activateRoomInviteInterface(); } );
+	buttonOpenGlobalLobby = std::make_shared<CButton>(Point(200, 360), AnimationPath::builtin("GSPBUT2.DEF"), CGI->generaltexth->zelp[105], [=](){ CSH->getGlobalLobby().activateInterface(); });
+
 	if(SEL->screenType == ESelectionScreen::campaignList)
 	{
 		labelCampaignDescription = std::make_shared<CLabel>(26, 132, FONT_SMALL, ETextAlignment::TOPLEFT, Colors::YELLOW, CGI->generaltexth->allTexts[38]);
@@ -183,8 +187,7 @@ InfoCard::InfoCard()
 		labelDifficulty = std::make_shared<CLabel>(62, 472, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE);
 		labelDifficultyPercent = std::make_shared<CLabel>(311, 472, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE);
 
-		labelGroupPlayersAssigned = std::make_shared<CLabelGroup>(FONT_SMALL, ETextAlignment::TOPLEFT, Colors::WHITE);
-		labelGroupPlayersUnassigned = std::make_shared<CLabelGroup>(FONT_SMALL, ETextAlignment::TOPLEFT, Colors::WHITE);
+		labelGroupPlayers = std::make_shared<CLabelGroup>(FONT_SMALL, ETextAlignment::TOPLEFT, Colors::WHITE);
 		disableLabelRedraws();
 	}
 	setChat(false);
@@ -240,27 +243,21 @@ void InfoCard::changeSelection()
 
 	OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
 	// FIXME: We recreate them each time because CLabelGroup don't use smart pointers
-	labelGroupPlayersAssigned = std::make_shared<CLabelGroup>(FONT_SMALL, ETextAlignment::TOPLEFT, Colors::WHITE);
-	labelGroupPlayersUnassigned = std::make_shared<CLabelGroup>(FONT_SMALL, ETextAlignment::TOPLEFT, Colors::WHITE);
+	labelGroupPlayers = std::make_shared<CLabelGroup>(FONT_SMALL, ETextAlignment::TOPLEFT, Colors::WHITE);
 	if(!showChat)
-	{
-		labelGroupPlayersAssigned->disable();
-		labelGroupPlayersUnassigned->disable();
-	}
+		labelGroupPlayers->disable();
+
 	for(auto & p : CSH->playerNames)
 	{
-		const auto pset = CSH->si->getPlayersSettings(p.first);
-		int pid = p.first;
-		if(pset)
-		{
-			auto name = boost::str(boost::format("%s (%d-%d %s)") % p.second.name % p.second.connection % pid % pset->color.toString());
-			labelGroupPlayersAssigned->add(24, 285 + (int)labelGroupPlayersAssigned->currentSize()*(int)graphics->fonts[FONT_SMALL]->getLineHeight(), name);
-		}
+		int slotsUsed = labelGroupPlayers->currentSize();
+		Point labelPosition;
+
+		if (slotsUsed < 4)
+			labelPosition = Point(24, 285 + slotsUsed * graphics->fonts[FONT_SMALL]->getLineHeight()); // left column
 		else
-		{
-			auto name = boost::str(boost::format("%s (%d-%d)") % p.second.name % p.second.connection % pid);
-			labelGroupPlayersUnassigned->add(193, 285 + (int)labelGroupPlayersUnassigned->currentSize()*(int)graphics->fonts[FONT_SMALL]->getLineHeight(), name);
-		}
+			labelPosition = Point(193, 285 + (slotsUsed - 4) * graphics->fonts[FONT_SMALL]->getLineHeight()); // right column
+
+		labelGroupPlayers->add(labelPosition.x, labelPosition.y, p.second.name);
 	}
 }
 
@@ -289,8 +286,12 @@ void InfoCard::setChat(bool activateChat)
 			labelVictoryConditionText->disable();
 			iconsLossCondition->disable();
 			labelLossConditionText->disable();
-			labelGroupPlayersAssigned->enable();
-			labelGroupPlayersUnassigned->enable();
+			labelGroupPlayers->enable();
+		}
+		if (CSH->inLobbyRoom())
+		{
+			buttonInvitePlayers->enable();
+			buttonOpenGlobalLobby->enable();
 		}
 		mapDescription->disable();
 		chat->enable();
@@ -298,6 +299,8 @@ void InfoCard::setChat(bool activateChat)
 	}
 	else
 	{
+		buttonInvitePlayers->disable();
+		buttonOpenGlobalLobby->disable();
 		mapDescription->enable();
 		chat->disable();
 		playerListBg->disable();
@@ -315,8 +318,7 @@ void InfoCard::setChat(bool activateChat)
 			iconsLossCondition->enable();
 			labelVictoryConditionText->enable();
 			labelLossConditionText->enable();
-			labelGroupPlayersAssigned->disable();
-			labelGroupPlayersUnassigned->disable();
+			labelGroupPlayers->disable();
 		}
 	}
 

+ 3 - 2
client/lobby/CSelectionBase.h

@@ -104,8 +104,9 @@ class InfoCard : public CIntObject
 	std::shared_ptr<CLabel> labelVictoryConditionText;
 	std::shared_ptr<CLabel> labelLossConditionText;
 
-	std::shared_ptr<CLabelGroup> labelGroupPlayersAssigned;
-	std::shared_ptr<CLabelGroup> labelGroupPlayersUnassigned;
+	std::shared_ptr<CLabelGroup> labelGroupPlayers;
+	std::shared_ptr<CButton> buttonInvitePlayers;
+	std::shared_ptr<CButton> buttonOpenGlobalLobby;
 public:
 
 	bool showChat;