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