1 /***************************************************************************
2  *      Mechanized Assault and Exploration Reloaded Projectfile            *
3  *                                                                         *
4  *   This program is free software; you can redistribute it and/or modify  *
5  *   it under the terms of the GNU General Public License as published by  *
6  *   the Free Software Foundation; either version 2 of the License, or     *
7  *   (at your option) any later version.                                   *
8  *                                                                         *
9  *   This program is distributed in the hope that it will be useful,       *
10  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
11  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
12  *   GNU General Public License for more details.                          *
13  *                                                                         *
14  *   You should have received a copy of the GNU General Public License     *
15  *   along with this program; if not, write to the                         *
16  *   Free Software Foundation, Inc.,                                       *
17  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
18  ***************************************************************************/
19 
20 #include "ui/graphical/menu/windows/windownetworklobby/windownetworklobby.h"
21 #include "ui/graphical/menu/windows/windowgamesettings/gamesettings.h"
22 #include "ui/graphical/menu/widgets/label.h"
23 #include "ui/graphical/menu/widgets/pushbutton.h"
24 #include "ui/graphical/menu/widgets/lineedit.h"
25 #include "ui/graphical/menu/widgets/image.h"
26 #include "ui/graphical/menu/widgets/listview.h"
27 #include "ui/graphical/menu/widgets/special/lobbychatboxlistviewitem.h"
28 #include "ui/graphical/menu/widgets/special/lobbyplayerlistviewitem.h"
29 #include "ui/graphical/menu/widgets/tools/validatorint.h"
30 #include "ui/graphical/menu/dialogs/dialogcolorpicker.h"
31 #include "game/data/player/player.h"
32 #include "pcx.h"
33 #include "main.h"
34 #include "network.h"
35 #include "game/data/map/map.h"
36 #include "video.h"
37 #include "game/logic/savegame.h"
38 #include "network.h"
39 #include "menuevents.h"
40 #include "maxrversion.h"
41 
42 //------------------------------------------------------------------------------
cWindowNetworkLobby(const std::string title,bool disableIp)43 cWindowNetworkLobby::cWindowNetworkLobby (const std::string title, bool disableIp) :
44 	cWindow (LoadPCX (GFXOD_MULT)),
45 	localPlayer (std::make_shared<cPlayerBasicData> (cSettings::getInstance().getPlayerName(), cPlayerColor (cSettings::getInstance().getPlayerColor()), 0, MAX_CLIENTS))
46 {
47 	addChild (std::make_unique<cLabel> (cBox<cPosition> (getPosition() + cPosition (0, 11), getPosition() + cPosition (getArea().getMaxCorner().x(), 11 + 10)), title, FONT_LATIN_NORMAL, eAlignmentType::CenterHorizontal));
48 
49 	mapImage = addChild (std::make_unique<cImage> (getPosition() + cPosition (33, 106)));
50 	mapNameLabel = addChild (std::make_unique<cLabel> (cBox<cPosition> (getPosition() + cPosition (90 - 70, 65), getPosition() + cPosition (90 + 70, 65 + 10)), "", FONT_LATIN_NORMAL, eAlignmentType::CenterHorizontal));
51 
52 	settingsTextLabel = addChild (std::make_unique<cLabel> (cBox<cPosition> (getPosition() + cPosition (192, 52), getPosition() + cPosition (192 + 246, 52 + 175)), ""));
53 	settingsTextLabel->setWordWrap (true);
54 
55 	chatLineEdit = addChild (std::make_unique<cLineEdit> (cBox<cPosition> (getPosition() + cPosition (20, 424), getPosition() + cPosition (20 + 430, 424 + 10))));
56 	signalConnectionManager.connect (chatLineEdit->returnPressed, std::bind (&cWindowNetworkLobby::triggerChatMessage, this, true));
57 	auto sendButton = addChild (std::make_unique<cPushButton> (getPosition() + cPosition (470, 416), ePushButtonType::StandardSmall, lngPack.i18n ("Text~Title~Send")));
58 	signalConnectionManager.connect (sendButton->clicked, std::bind (&cWindowNetworkLobby::triggerChatMessage, this, false));
59 	chatList = addChild (std::make_unique<cListView<cLobbyChatBoxListViewItem>> (cBox<cPosition> (getPosition() + cPosition (14, 284), getPosition() + cPosition (14 + 439, 284 + 124)), eScrollBarStyle::Classic));
60 	chatList->disableSelectable();
61 	chatList->setBeginMargin (cPosition (12, 12));
62 	chatList->setEndMargin (cPosition (10, 10));
63 	chatList->setScrollOffset (font->getFontHeight() + 3);
64 
65 	addChild (std::make_unique<cLabel> (cBox<cPosition> (getPosition() + cPosition (20, 245), getPosition() + cPosition (20 + 170, 245 + 10)), lngPack.i18n ("Text~Title~IP"), FONT_LATIN_NORMAL, eAlignmentType::Left));
66 	addChild (std::make_unique<cLabel> (cBox<cPosition> (getPosition() + cPosition (228, 245), getPosition() + cPosition (228 + 90, 245 + 10)), lngPack.i18n ("Text~Title~Port"), FONT_LATIN_NORMAL, eAlignmentType::Left));
67 	addChild (std::make_unique<cLabel> (cBox<cPosition> (getPosition() + cPosition (352, 245), getPosition() + cPosition (352 + 90, 245 + 10)), lngPack.i18n ("Text~Title~Player_Name"), FONT_LATIN_NORMAL, eAlignmentType::Left));
68 	addChild (std::make_unique<cLabel> (cBox<cPosition> (getPosition() + cPosition (500, 245), getPosition() + cPosition (500 + 90, 245 + 10)), lngPack.i18n ("Text~Title~Color"), FONT_LATIN_NORMAL, eAlignmentType::Left));
69 
70 	ipLineEdit = addChild (std::make_unique<cLineEdit> (cBox<cPosition> (getPosition() + cPosition (20, 260), getPosition() + cPosition (20 + 178, 260 + 10))));
71 	if (disableIp)
72 	{
73 		ipLineEdit->setText ("-");
74 		ipLineEdit->setReadOnly (true);
75 	}
76 	else
77 	{
78 		ipLineEdit->setText(cSettings::getInstance().getIP());
79 	}
80 	portLineEdit = addChild (std::make_unique<cLineEdit> (cBox<cPosition> (getPosition() + cPosition (230, 260), getPosition() + cPosition (230 + 95, 260 + 10))));
81 	portLineEdit->setText (iToStr (cSettings::getInstance().getPort()));
82 	portLineEdit->setValidator (std::make_unique<cValidatorInt> (0, 65535));
83 	const auto restoreDefaultPortButton = addChild(std::make_unique<cImage>(getPosition() + cPosition(230 + 82, 254), GraphicsData.gfx_Cpfeil2.get()));
84 	signalConnectionManager.connect(restoreDefaultPortButton->clicked, [this]()
85 	{
86 		portLineEdit->setText(iToStr(DEFAULTPORT));
87 	});
88 
89 	auto nameLineEdit = addChild (std::make_unique<cLineEdit> (cBox<cPosition> (getPosition() + cPosition (353, 260), getPosition() + cPosition (353 + 95, 260 + 10))));
90 	nameLineEdit->setText (localPlayer->getName());
91 	signalConnectionManager.connect (nameLineEdit->returnPressed, [this, nameLineEdit]()
92 	{
93 		auto application = getActiveApplication();
94 		if (application) application->releaseKeyFocus (*nameLineEdit);
95 	});
96 	signalConnectionManager.connect (nameLineEdit->editingFinished, [&, nameLineEdit] (eValidatorState) {localPlayer->setName (nameLineEdit->getText()); });
97 
98 	playersList = addChild (std::make_unique<cListView<cLobbyPlayerListViewItem>> (cBox<cPosition> (getPosition() + cPosition (465, 284), getPosition() + cPosition (465 + 167, 284 + 124)), eScrollBarStyle::Classic));
99 	playersList->disableSelectable();
100 	playersList->setBeginMargin (cPosition (12, 12));
101 	playersList->setEndMargin (cPosition (10, 10));
102 	playersList->setItemDistance (4);
103 
104 	colorImage = addChild (std::make_unique<cImage> (getPosition() + cPosition (505, 260)));
105 	signalConnectionManager.connect (colorImage->clicked, [this]()
106 	{
107 		auto application = getActiveApplication();
108 
109 		if (!application) return;
110 
111 		auto dialog = application->show (std::make_shared<cDialogColorPicker> (localPlayer->getColor().getColor()));
112 		dialog->done.connect ([this, dialog]()
113 		{
114 			localPlayer->setColor (cPlayerColor (dialog->getSelectedColor()));
115 			dialog->close();
116 		});
117 		dialog->canceled.connect ([dialog]() { dialog->close(); });
118 	});
119 	auto nextColorButton = addChild (std::make_unique<cPushButton> (getPosition() + cPosition (596, 256), ePushButtonType::ArrowRightSmall, &SoundData.SNDObjectMenu));
120 	signalConnectionManager.connect (nextColorButton->clicked, [this]()
121 	{
122 		const auto localPlayerColorIndex = (cPlayerColor::findClosestPredefinedColor (localPlayer->getColor().getColor()) + 1) % cPlayerColor::predefinedColorsCount;
123 		localPlayer->setColor (cPlayerColor (cPlayerColor::predefinedColors[localPlayerColorIndex]));
124 	});
125 	auto prevColorButton = addChild (std::make_unique<cPushButton> (getPosition() + cPosition (478, 256), ePushButtonType::ArrowLeftSmall, &SoundData.SNDObjectMenu));
126 	signalConnectionManager.connect (prevColorButton->clicked, [this]()
127 	{
128 		const auto localPlayerColorIndex = (cPlayerColor::findClosestPredefinedColor (localPlayer->getColor().getColor()) + cPlayerColor::predefinedColorsCount - 1) % cPlayerColor::predefinedColorsCount;
129 		localPlayer->setColor (cPlayerColor (cPlayerColor::predefinedColors[localPlayerColorIndex]));
130 	});
131 
132 	auto backButton = addChild (std::make_unique<cPushButton> (getPosition() + cPosition (50, 450), ePushButtonType::StandardBig, lngPack.i18n ("Text~Others~Back")));
133 	signalConnectionManager.connect (backButton->clicked, [&]() { backClicked(); });
134 
135 	updateSettingsText();
136 	updateMap();
137 	updatePlayerColor();
138 
139 	localPlayer->colorChanged.connect (std::bind (&cWindowNetworkLobby::updatePlayerColor, this));
140 
141 	addPlayer (localPlayer);
142 }
143 
144 //------------------------------------------------------------------------------
updateSettingsText()145 void cWindowNetworkLobby::updateSettingsText()
146 {
147 	std::string text = lngPack.i18n ("Text~Main~Version", PACKAGE_VERSION) + "\n";
148 	//text += "Checksum: " + iToStr (cSettings::getInstance().Checksum) + "\n\n";
149 
150 	if (saveGamePlayers.size() != 0)
151 	{
152 		text += lngPack.i18n ("Text~Title~Savegame") + ":\n  " + saveGameName +  "\n\n" + lngPack.i18n ("Text~Title~Players") + "\n";
153 		for (size_t i = 0; i < saveGamePlayers.size(); ++i)
154 		{
155 			text += saveGamePlayers[i].getName() + "\n";
156 		}
157 		text += "\n";
158 	}
159 	if (staticMap != nullptr)
160 	{
161 		text += lngPack.i18n ("Text~Title~Map") + lngPack.i18n ("Text~Punctuation~Colon") + staticMap->getName();
162 		text += " (" + iToStr (staticMap->getSize().x()) + "x" + iToStr (staticMap->getSize().y()) + ")\n";
163 	}
164 	else if (saveGamePlayers.size() == 0) text += lngPack.i18n ("Text~Multiplayer~Map_NoSet") + "\n";
165 
166 	text += "\n";
167 
168 	if (saveGamePlayers.size() == 0)
169 	{
170 		if (gameSettings)
171 		{
172 			auto additionalGameEndString = gameSettings->getVictoryCondition() == eGameSettingsVictoryCondition::Turns ? (" " + iToStr (gameSettings->getVictoryTurns()) + " ") : (gameSettings->getVictoryCondition() == eGameSettingsVictoryCondition::Points ? (" " + iToStr (gameSettings->getVictoryPoints()) + " ") : " ");
173 			text += lngPack.i18n ("Text~Comp~GameEndsAt") + additionalGameEndString + gameSettingsVictoryConditionToString (gameSettings->getVictoryCondition(), true) + "\n";
174 			text += lngPack.i18n ("Text~Title~Metal") + lngPack.i18n ("Text~Punctuation~Colon") + gameSettingsResourceAmountToString (gameSettings->getMetalAmount(), true) + "\n";
175 			text += lngPack.i18n ("Text~Title~Oil") + lngPack.i18n ("Text~Punctuation~Colon") + gameSettingsResourceAmountToString (gameSettings->getOilAmount(), true) + "\n";
176 			text += lngPack.i18n ("Text~Title~Gold") + lngPack.i18n ("Text~Punctuation~Colon") + gameSettingsResourceAmountToString (gameSettings->getGoldAmount(), true) + "\n";
177 			text += lngPack.i18n ("Text~Title~Resource_Density") + lngPack.i18n ("Text~Punctuation~Colon") + gameSettingsResourceDensityToString (gameSettings->getResourceDensity(), true) + "\n";
178 			text += lngPack.i18n ("Text~Title~Credits")  + lngPack.i18n ("Text~Punctuation~Colon") + iToStr (gameSettings->getStartCredits()) + "\n";
179 			text += lngPack.i18n ("Text~Title~BridgeHead") + lngPack.i18n ("Text~Punctuation~Colon") + gameSettingsBridgeheadTypeToString (gameSettings->getBridgeheadType(), true) + "\n";
180 			text += lngPack.i18n ("Text~Title~Clans") + lngPack.i18n ("Text~Punctuation~Colon") + (gameSettings->getClansEnabled() ? lngPack.i18n ("Text~Option~On") : lngPack.i18n ("Text~Option~Off")) + "\n";
181 			text += lngPack.i18n ("Text~Title~Game_Type") + lngPack.i18n ("Text~Punctuation~Colon") + gameSettingsGameTypeToString (gameSettings->getGameType(), true) + "\n";
182 			text += lngPack.i18n ("Text~Title~Turn_limit") + lngPack.i18n ("Text~Punctuation~Colon") + (gameSettings->isTurnLimitActive() ? iToStr (gameSettings->getTurnLimit().count()) + "s" : lngPack.i18n ("Text~Settings~Unlimited_11")) + "\n";
183 			text += lngPack.i18n ("Text~Title~Turn_end") + lngPack.i18n ("Text~Punctuation~Colon") + (gameSettings->isTurnEndDeadlineActive() ? iToStr (gameSettings->getTurnEndDeadline().count()) + "s" : lngPack.i18n ("Text~Settings~Unlimited_11")) + "\n";
184 		}
185 		else text += lngPack.i18n ("Text~Multiplayer~Option_NoSet") + "\n";
186 	}
187 	settingsTextLabel->setText (text);
188 }
189 
190 //------------------------------------------------------------------------------
updateMap()191 void cWindowNetworkLobby::updateMap()
192 {
193 	if (!staticMap || !staticMap->isValid())
194 	{
195 		mapImage->setImage (nullptr);
196 		mapNameLabel->setText ("");
197 		return;
198 	}
199 
200 	AutoSurface surface (cStaticMap::loadMapPreview (staticMap->getName()));
201 	if (surface != nullptr)
202 	{
203 		mapImage->setImage (surface.get());
204 	}
205 
206 	auto mapName = staticMap->getName();
207 	const auto size = staticMap->getSize();
208 
209 	if (font->getTextWide (">" + mapName.substr (0, mapName.length() - 4) + " (" + iToStr (size.x()) + "x" + iToStr (size.y()) + ")<") > 140)
210 	{
211 		while (font->getTextWide (">" + mapName + "... (" + iToStr (size.x()) + "x" + iToStr (size.y()) + ")<") > 140)
212 		{
213 			mapName.erase (mapName.length() - 1, mapName.length());
214 		}
215 		mapName = mapName + "... (" + iToStr (size.x()) + "x" + iToStr (size.y()) + ")";
216 	}
217 	else mapName = mapName.substr (0, mapName.length() - 4) + " (" + iToStr (size.x()) + "x" + iToStr (size.y()) + ")";
218 
219 	mapNameLabel->setText (mapName);
220 }
221 
222 //------------------------------------------------------------------------------
updatePlayerColor()223 void cWindowNetworkLobby::updatePlayerColor()
224 {
225 	SDL_Rect src = {0, 0, 83, 10};
226 	AutoSurface colorSurface (SDL_CreateRGBSurface (0, src.w, src.h, Video.getColDepth(), 0, 0, 0, 0));
227 	SDL_BlitSurface (localPlayer->getColor().getTexture(), &src, colorSurface.get(), nullptr);
228 	colorImage->setImage (colorSurface.get());
229 }
230 //------------------------------------------------------------------------------
triggerChatMessage(bool keepFocus)231 void cWindowNetworkLobby::triggerChatMessage (bool keepFocus)
232 {
233 	if (!chatLineEdit->getText().empty())
234 	{
235 		triggeredChatMessage();
236 		chatLineEdit->setText ("");
237 	}
238 	if (!keepFocus)
239 	{
240 		auto application = getActiveApplication();
241 		if (application)
242 		{
243 			application->releaseKeyFocus (*chatLineEdit);
244 		}
245 	}
246 }
247 
248 //------------------------------------------------------------------------------
addChatEntry(const std::string & playerName,const std::string & message)249 void cWindowNetworkLobby::addChatEntry (const std::string& playerName, const std::string& message)
250 {
251 	auto addedItem = chatList->addItem (std::make_unique<cLobbyChatBoxListViewItem> (playerName, message), eAddListItemScrollType::IfAtBottom);
252 	cSoundDevice::getInstance().playSoundEffect (SoundData.SNDChat);
253 }
254 
255 //------------------------------------------------------------------------------
addInfoEntry(const std::string & message)256 void cWindowNetworkLobby::addInfoEntry (const std::string& message)
257 {
258 	auto addedItem = chatList->addItem (std::make_unique<cLobbyChatBoxListViewItem> (message), eAddListItemScrollType::IfAtBottom);
259 }
260 
261 //------------------------------------------------------------------------------
addPlayer(const std::shared_ptr<cPlayerBasicData> & player)262 void cWindowNetworkLobby::addPlayer (const std::shared_ptr<cPlayerBasicData>& player)
263 {
264 	auto item = playersList->addItem (std::make_unique<cLobbyPlayerListViewItem> (player));
265 	if (player == localPlayer)
266 	{
267 		signalConnectionManager.connect (item->readyClicked, [player, this]()
268 		{
269 			cSoundDevice::getInstance().playSoundEffect (SoundData.SNDHudButton);
270 			wantLocalPlayerReadyChange();
271 		});
272 	}
273 }
274 
275 //------------------------------------------------------------------------------
removePlayer(const cPlayerBasicData & player)276 void cWindowNetworkLobby::removePlayer (const cPlayerBasicData& player)
277 {
278 	if (&player == localPlayer.get()) return; // do never remove the local player
279 
280 	for (size_t i = 0; i < playersList->getItemsCount(); ++i)
281 	{
282 		auto& item = playersList->getItem (i);
283 		if (item.getPlayer().get() == &player)
284 		{
285 			playersList->removeItem (item);
286 			break;
287 		}
288 	}
289 }
290 
291 //------------------------------------------------------------------------------
removeNonLocalPlayers()292 void cWindowNetworkLobby::removeNonLocalPlayers()
293 {
294 	for (size_t i = 0; i < playersList->getItemsCount();)
295 	{
296 		auto& item = playersList->getItem (i);
297 		if (item.getPlayer().get() != localPlayer.get())
298 		{
299 			playersList->removeItem (item);
300 		}
301 		else
302 		{
303 			++i;
304 		}
305 	}
306 }
307 
308 //------------------------------------------------------------------------------
setStaticMap(std::shared_ptr<cStaticMap> staticMap_)309 void cWindowNetworkLobby::setStaticMap (std::shared_ptr<cStaticMap> staticMap_)
310 {
311 	staticMap = std::move (staticMap_);
312 
313 	updateMap();
314 	updateSettingsText();
315 	staticMapChanged();
316 }
317 
318 //------------------------------------------------------------------------------
setGameSettings(std::unique_ptr<cGameSettings> gameSettings_)319 void cWindowNetworkLobby::setGameSettings (std::unique_ptr<cGameSettings> gameSettings_)
320 {
321 	gameSettings = std::move (gameSettings_);
322 
323 	updateSettingsText();
324 	gameSettingsChanged();
325 }
326 
327 //------------------------------------------------------------------------------
setMapDownloadPercent(int percent)328 void cWindowNetworkLobby::setMapDownloadPercent (int percent)
329 {
330 	mapNameLabel->setText (lngPack.i18n ("Text~Multiplayer~MapDL_Percent", iToStr (percent)));
331 }
332 
333 //------------------------------------------------------------------------------
setMapDownloadCanceled()334 void cWindowNetworkLobby::setMapDownloadCanceled()
335 {
336 	mapNameLabel->setText (lngPack.i18n ("Text~Multiplayer~MapDL_Cancel"));
337 }
338 
339 //------------------------------------------------------------------------------
getGameSettings() const340 const std::shared_ptr<cGameSettings>& cWindowNetworkLobby::getGameSettings() const
341 {
342 	return gameSettings;
343 }
344 
345 //------------------------------------------------------------------------------
getStaticMap() const346 const std::shared_ptr<cStaticMap>& cWindowNetworkLobby::getStaticMap() const
347 {
348 	return staticMap;
349 }
350 
351 //------------------------------------------------------------------------------
getSaveGamePlayers() const352 const std::vector<cPlayerBasicData>& cWindowNetworkLobby::getSaveGamePlayers() const
353 {
354 	return saveGamePlayers;
355 }
356 
357 //------------------------------------------------------------------------------
getSaveGameName() const358 std::string cWindowNetworkLobby::getSaveGameName() const
359 {
360 	return saveGameName;
361 }
362 
363 //------------------------------------------------------------------------------
getLocalPlayer() const364 const std::shared_ptr<cPlayerBasicData>& cWindowNetworkLobby::getLocalPlayer() const
365 {
366 	return localPlayer;
367 }
368 
369 //------------------------------------------------------------------------------
getPlayers() const370 std::vector<std::shared_ptr<cPlayerBasicData>> cWindowNetworkLobby::getPlayers() const
371 {
372 	std::vector<std::shared_ptr<cPlayerBasicData>> result;
373 	for (size_t i = 0; i < playersList->getItemsCount(); ++i)
374 	{
375 		const auto& item = playersList->getItem (i);
376 		result.push_back (item.getPlayer());
377 	}
378 	return result;
379 }
380 
381 //------------------------------------------------------------------------------
getPlayersNotShared() const382 std::vector<cPlayerBasicData> cWindowNetworkLobby::getPlayersNotShared() const
383 {
384 	std::vector<cPlayerBasicData> result;
385 	for (size_t i = 0; i < playersList->getItemsCount(); ++i)
386 	{
387 		const auto& item = playersList->getItem (i);
388 		result.push_back (*item.getPlayer());
389 	}
390 	return result;
391 }
392 
393 //------------------------------------------------------------------------------
getPort() const394 unsigned short cWindowNetworkLobby::getPort() const
395 {
396 	return atoi (portLineEdit->getText().c_str());
397 }
398 
399 //------------------------------------------------------------------------------
getIp() const400 const std::string& cWindowNetworkLobby::getIp() const
401 {
402 	return ipLineEdit->getText();
403 }
404 
405 //------------------------------------------------------------------------------
getChatMessage() const406 const std::string& cWindowNetworkLobby::getChatMessage() const
407 {
408 	return chatLineEdit->getText();
409 }
410 
411 //------------------------------------------------------------------------------
disablePortEdit()412 void cWindowNetworkLobby::disablePortEdit()
413 {
414 	portLineEdit->disable();
415 }
416 
417 //------------------------------------------------------------------------------
disableIpEdit()418 void cWindowNetworkLobby::disableIpEdit()
419 {
420 	ipLineEdit->disable();
421 }
422