1 /*
2 * Hedgewars, a free turn based strategy game
3 * Copyright (c) 2004-2015 Andrey Korotaev <unC0Rr@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; version 2 of the License
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 Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18
19 #include <QDir>
20 #include <QFile>
21 #include <QTextStream>
22 #include <QMessageBox>
23 #include <QPushButton>
24 #include <QSpinBox>
25 #include <QListWidget>
26 #include <QStackedLayout>
27 #include <QLineEdit>
28 #include <QLabel>
29 #include <QRadioButton>
30 #include <QSpinBox>
31 #include <QCloseEvent>
32 #include <QCheckBox>
33 #include <QTextBrowser>
34 #include <QAction>
35 #include <QTimer>
36 #include <QScrollBar>
37 #include <QDataWidgetMapper>
38 #include <QTableView>
39 #include <QCryptographicHash>
40 #include <QSignalMapper>
41 #include <QShortcut>
42 #include <QDesktopServices>
43 #include <QDesktopWidget>
44 #include <QApplication>
45 #include <QInputDialog>
46 #include <QPropertyAnimation>
47 #include <QSettings>
48 #include <QSortFilterProxyModel>
49 #include <QIcon>
50 #include <QImage>
51
52 #if (QT_VERSION >= 0x040600)
53 #include <QGraphicsEffect>
54 #include <QParallelAnimationGroup>
55 #endif
56
57 #include "hwform.h"
58 #include "game.h"
59 #include "team.h"
60 #include "mission.h"
61 #include "campaign.h"
62 #include "teamselect.h"
63 #include "selectWeapon.h"
64 #include "gameuiconfig.h"
65 #include "pageinfo.h"
66 #include "pagetraining.h"
67 #include "pagesingleplayer.h"
68 #include "pageselectweapon.h"
69 #include "pageadmin.h"
70 #include "pagecampaign.h"
71 #include "pagescheme.h"
72 #include "pagenetgame.h"
73 #include "pageroomslist.h"
74 #include "pageconnecting.h"
75 #include "pageoptions.h"
76 #include "pageeditteam.h"
77 #include "pagemultiplayer.h"
78 #include "pagenet.h"
79 #include "pagemain.h"
80 #include "pagenetserver.h"
81 #include "pagedrawmap.h"
82 #include "pagegamestats.h"
83 #include "pageplayrecord.h"
84 #include "pagedata.h"
85 #include "pagevideos.h"
86 #include "hwconsts.h"
87 #include "newnetclient.h"
88 #include "gamecfgwidget.h"
89 #include "netserverslist.h"
90 #include "netudpserver.h"
91 #include "chatwidget.h"
92 #include "input_ip.h"
93 #include "input_password.h"
94 #include "gameSchemeModel.h"
95 #include "bgwidget.h"
96 #include "drawmapwidget.h"
97 #include "mouseoverfilter.h"
98 #include "roomslistmodel.h"
99 #include "recorder.h"
100 #include "playerslistmodel.h"
101 #include "feedbackdialog.h"
102
103 #include "MessageDialog.h"
104 #include "DataManager.h"
105 #include "AutoUpdater.h"
106
107 #ifdef Q_OS_WIN
108 #ifndef WINVER
109 #define WINVER 0x0500
110 #endif
111 #include <windows.h>
112 #else
113 #include <unistd.h>
114 #include <sys/types.h>
115 #endif
116
117 #ifdef Q_OS_MAC
118 #include <sys/sysctl.h>
119 #endif
120
121 #ifdef __APPLE__
122 #include "M3Panel.h"
123 #ifdef SPARKLE_ENABLED
124 #include "SparkleAutoUpdater.h"
125 #endif
126 #endif
127
128
129 // I started handing this down to each place it touches, but it was getting ridiculous
130 // and this one flag does not warrant a static class
131 bool frontendEffects = true;
132 bool demoIsPresent = true;
133 QString playerHash;
134
135 QIcon finishedIcon;
136 QIcon notFinishedIcon;
137 GameUIConfig* HWForm::config = NULL;
138
HWForm(QWidget * parent,QString styleSheet)139 HWForm::HWForm(QWidget *parent, QString styleSheet)
140 : QMainWindow(parent)
141 , game(0)
142 , pnetserver(0)
143 , pRegisterServer(0)
144 , editedTeam(0)
145 , hwnet(0)
146 {
147 // set music track
148 SDLInteraction::instance().setMusicTrack("/Music/main_theme.ogg");
149
150 this->setStyleSheet(styleSheet);
151
152
153 QIcon * hwIcon = new QIcon();
154 hwIcon->addFile(":/res/hh_small.png");
155 //hwIcon->addFile(":/res/hh25x25.png");
156 // crop-workaround for the fact that hh25x25.png is actually 25x35
157 QPixmap pm(":/res/hh25x25.png");
158 hwIcon->addPixmap(pm.copy(0,(pm.height()-25)/2,25,25));
159 hwIcon->addFile(":/res/teamicon.png");
160 hwIcon->addFile(":/res/teamicon2.png");
161
162 this->setWindowIcon(*hwIcon);
163 ui.setupUi(this);
164 setMinimumSize(760, 580);
165 //setFocusPolicy(Qt::StrongFocus);
166 CustomizePalettes();
167
168 ui.pageOptions->CBResolution->addItems(SDLInteraction::instance().getResolutions());
169
170 config = new GameUIConfig(this, DataManager::instance().settingsFileName());
171 frontendEffects = config->value("frontend/effects", true).toBool();
172 bool frontendSounds = config->value("frontend/sound", true).toBool();
173 onFrontendSoundsToggled(frontendSounds);
174
175 playerHash = QString(QCryptographicHash::hash(config->value("net/nick", config->getRandomNick()).toString().toUtf8(), QCryptographicHash::Md5).toHex());
176
177 // Icons for finished missions
178 finishedIcon.addFile(":/res/missionFinished.png", QSize(), QIcon::Normal, QIcon::On);
179 finishedIcon.addFile(":/res/missionFinishedSelected.png", QSize(), QIcon::Selected, QIcon::On);
180
181 // A transparent icon, used to nicely align the unfinished missions with the finished ones
182 QPixmap emptySpace = QPixmap(15, 15);
183 emptySpace.fill(QColor(0, 0, 0, 0));
184 notFinishedIcon = QIcon(emptySpace);
185
186 ui.pageRoomsList->setSettings(config);
187 ui.pageNetGame->setSettings(config);
188 ui.pageNetGame->chatWidget->setSettings(config);
189 ui.pageRoomsList->chatWidget->setSettings(config);
190 ui.pageOptions->setConfig(config);
191 #ifdef VIDEOREC
192 ui.pageVideos->init(config);
193 #endif
194
195 #if defined(__APPLE__) && defined(SPARKLE_ENABLED)
196 if (config->isAutoUpdateEnabled())
197 {
198 AutoUpdater* updater = NULL;
199
200 updater = new SparkleAutoUpdater();
201 if (updater)
202 {
203 updater->checkForUpdates();
204 delete updater;
205 }
206 }
207 #endif
208
209 #ifdef __APPLE__
210 panel = new M3Panel;
211
212 QShortcut *hideFrontend = new QShortcut(QKeySequence("Ctrl+M"), this);
213 connect (hideFrontend, SIGNAL(activated()), this, SLOT(showMinimized()));
214 #else
215 // ctrl+q closes frontend for consistency
216 QShortcut * closeFrontend = new QShortcut(QKeySequence("Ctrl+Q"), this);
217 connect (closeFrontend, SIGNAL(activated()), this, SLOT(close()));
218 //QShortcut * updateData = new QShortcut(QKeySequence("F5"), this);
219 //connect (updateData, SIGNAL(activated()), &DataManager::instance(), SLOT(reload()));
220 #endif
221
222 previousCampaignName = "";
223 previousTeamName = "";
224 UpdateTeamsLists();
225 InitCampaignPage();
226 RestoreSingleplayerTeamSelection();
227 UpdateCampaignPage(0);
228 UpdateCampaignPageTeam(0);
229 UpdateCampaignPageMission(0);
230 UpdateWeapons();
231
232 // connect all goBack signals
233 int nPages = ui.Pages->count();
234
235 for (int i = 0; i < nPages; i++)
236 connect(ui.Pages->widget(i), SIGNAL(goBack()), this, SLOT(GoBack()));
237
238 pageSwitchMapper = new QSignalMapper(this);
239 connect(pageSwitchMapper, SIGNAL(mapped(int)), this, SLOT(GoToPage(int)));
240
241 connect(ui.pageMain->BtnSinglePlayer, SIGNAL(clicked()), pageSwitchMapper, SLOT(map()));
242 pageSwitchMapper->setMapping(ui.pageMain->BtnSinglePlayer, ID_PAGE_SINGLEPLAYER);
243
244 connect(ui.pageMain->BtnSetup, SIGNAL(clicked()), pageSwitchMapper, SLOT(map()));
245 pageSwitchMapper->setMapping(ui.pageMain->BtnSetup, ID_PAGE_SETUP);
246
247 connect(ui.pageMain->BtnFeedback, SIGNAL(clicked()), this, SLOT(showFeedbackDialog()));
248
249 connect(ui.pageMain->BtnTitle, SIGNAL(clicked()), pageSwitchMapper, SLOT(map()));
250 pageSwitchMapper->setMapping(ui.pageMain->BtnTitle, ID_PAGE_INFO);
251
252 connect(ui.pageMain->BtnInfo, SIGNAL(clicked()), pageSwitchMapper, SLOT(map()));
253 pageSwitchMapper->setMapping(ui.pageMain->BtnInfo, ID_PAGE_INFO);
254
255 connect(ui.pageMain->BtnDataDownload, SIGNAL(clicked()), pageSwitchMapper, SLOT(map()));
256 pageSwitchMapper->setMapping(ui.pageMain->BtnDataDownload, ID_PAGE_DATADOWNLOAD);
257
258 connect(ui.pageMain->BtnHelp, SIGNAL(clicked()), this, SLOT(GoToHelp()));
259
260 #ifdef VIDEOREC
261 connect(ui.pageMain->BtnVideos, SIGNAL(clicked()), pageSwitchMapper, SLOT(map()));
262 pageSwitchMapper->setMapping(ui.pageMain->BtnVideos, ID_PAGE_VIDEOS);
263 #endif
264
265 //connect(ui.pageMain->BtnExit, SIGNAL(pressed()), this, SLOT(btnExitPressed()));
266 //connect(ui.pageMain->BtnExit, SIGNAL(clicked()), this, SLOT(btnExitClicked()));
267
268 connect(ui.pageEditTeam, SIGNAL(goBack()), this, SLOT(AfterTeamEdit()));
269
270 connect(ui.pageMultiplayer->BtnStartMPGame, SIGNAL(clicked()), this, SLOT(StartMPGame()));
271 connect(ui.pageMultiplayer->teamsSelect, SIGNAL(setEnabledGameStart(bool)),
272 ui.pageMultiplayer->BtnStartMPGame, SLOT(setEnabled(bool)));
273 connect(ui.pageMultiplayer, SIGNAL(SetupClicked()), this, SLOT(IntermediateSetup()));
274 connect(ui.pageMultiplayer->gameCFG, SIGNAL(goToSchemes(int)), this, SLOT(GoToScheme(int)));
275 connect(ui.pageMultiplayer->gameCFG, SIGNAL(goToWeapons(int)), this, SLOT(GoToWeapons(int)));
276 connect(ui.pageMultiplayer->gameCFG, SIGNAL(goToDrawMap()), pageSwitchMapper, SLOT(map()));
277 pageSwitchMapper->setMapping(ui.pageMultiplayer->gameCFG, ID_PAGE_DRAWMAP);
278
279
280 connect(ui.pagePlayDemo->BtnPlayDemo, SIGNAL(clicked()), this, SLOT(PlayDemo()));
281 connect(ui.pagePlayDemo->DemosList, SIGNAL(doubleClicked (const QModelIndex &)), this, SLOT(PlayDemo()));
282
283 connect(ui.pageOptions, SIGNAL(newTeamRequested()), this, SLOT(NewTeam()));
284 connect(ui.pageOptions, SIGNAL(editTeamRequested(const QString&)), this, SLOT(EditTeam(const QString&)));
285 connect(ui.pageOptions, SIGNAL(deleteTeamRequested(const QString&)), this, SLOT(DeleteTeam(const QString&)));
286 connect(ui.pageOptions, SIGNAL(goBack()), config, SLOT(SaveOptions()));
287 connect(ui.pageOptions->BtnAssociateFiles, SIGNAL(clicked()), this, SLOT(AssociateFiles()));
288
289 connect(ui.pageOptions->WeaponEdit, SIGNAL(clicked()), this, SLOT(GoToEditWeapons()));
290 connect(ui.pageOptions->WeaponNew, SIGNAL(clicked()), this, SLOT(GoToNewWeapons()));
291 connect(ui.pageOptions->WeaponDelete, SIGNAL(clicked()), this, SLOT(DeleteWeaponSet()));
292 connect(ui.pageOptions->SchemeEdit, SIGNAL(clicked()), this, SLOT(GoToEditScheme()));
293 connect(ui.pageOptions->SchemeNew, SIGNAL(clicked()), this, SLOT(GoToNewScheme()));
294 connect(ui.pageOptions->SchemeDelete, SIGNAL(clicked()), this, SLOT(DeleteScheme()));
295 connect(ui.pageOptions->CBFrontendEffects, SIGNAL(toggled(bool)), this, SLOT(onFrontendEffects(bool)) );
296 connect(ui.pageOptions->CBFrontendSound, SIGNAL(toggled(bool)), this, SLOT(onFrontendSoundsToggled(bool)));
297
298 connect(ui.pageNet->BtnSpecifyServer, SIGNAL(clicked()), this, SLOT(NetConnect()));
299 connect(ui.pageNet->BtnNetSvrStart, SIGNAL(clicked()), pageSwitchMapper, SLOT(map()));
300 pageSwitchMapper->setMapping(ui.pageNet->BtnNetSvrStart, ID_PAGE_NETSERVER);
301
302 connect(ui.pageNet, SIGNAL(connectClicked(const QString &, quint16, bool)), this, SLOT(NetConnectServer(const QString &, quint16, bool)));
303
304 connect(ui.pageNetServer->BtnStart, SIGNAL(clicked()), this, SLOT(NetStartServer()));
305
306 connect(ui.pageNetGame->pNetTeamsWidget, SIGNAL(setEnabledGameStart(bool)),
307 ui.pageNetGame->BtnStart, SLOT(setEnabled(bool)));
308 connect(ui.pageNetGame, SIGNAL(SetupClicked()), this, SLOT(IntermediateSetup()));
309 connect(ui.pageNetGame->pGameCFG, SIGNAL(goToSchemes(int)), this, SLOT(GoToScheme(int)));
310 connect(ui.pageNetGame->pGameCFG, SIGNAL(goToWeapons(int)), this, SLOT(GoToWeapons(int)));
311 connect(ui.pageNetGame->pGameCFG, SIGNAL(goToDrawMap()), pageSwitchMapper, SLOT(map()));
312 pageSwitchMapper->setMapping(ui.pageNetGame->pGameCFG, ID_PAGE_DRAWMAP);
313
314 connect(ui.pageRoomsList->BtnAdmin, SIGNAL(clicked()), pageSwitchMapper, SLOT(map()));
315 pageSwitchMapper->setMapping(ui.pageRoomsList->BtnAdmin, ID_PAGE_ADMIN);
316
317 connect(ui.pageInfo->BtnSnapshots, SIGNAL(clicked()), this, SLOT(OpenSnapshotFolder()));
318
319 connect(ui.pageGameStats, SIGNAL(saveDemoRequested()), this, SLOT(saveDemoWithCustomName()));
320 connect(ui.pageGameStats, SIGNAL(restartGameRequested()), this, SLOT(restartGame()));
321
322 connect(ui.pageSinglePlayer->BtnSimpleGamePage, SIGNAL(clicked()), this, SLOT(SimpleGame()));
323 connect(ui.pageSinglePlayer->BtnTrainPage, SIGNAL(clicked()), pageSwitchMapper, SLOT(map()));
324 pageSwitchMapper->setMapping(ui.pageSinglePlayer->BtnTrainPage, ID_PAGE_TRAINING);
325
326 connect(ui.pageSinglePlayer->BtnCampaignPage, SIGNAL(clicked()), pageSwitchMapper, SLOT(map()));
327 pageSwitchMapper->setMapping(ui.pageSinglePlayer->BtnCampaignPage, ID_PAGE_CAMPAIGN);
328
329 connect(ui.pageSinglePlayer->BtnMultiplayer, SIGNAL(clicked()), pageSwitchMapper, SLOT(map()));
330 pageSwitchMapper->setMapping(ui.pageSinglePlayer->BtnMultiplayer, ID_PAGE_MULTIPLAYER);
331
332 connect(ui.pageSinglePlayer->BtnLoad, SIGNAL(clicked()), this, SLOT(GoToSaves()));
333 connect(ui.pageSinglePlayer->BtnDemos, SIGNAL(clicked()), this, SLOT(GoToDemos()));
334
335 connect(ui.pageTraining, SIGNAL(startMission(const QString&, const QString&)), this, SLOT(startTraining(const QString&, const QString&)));
336
337 connect(ui.pageCampaign->BtnStartCampaign, SIGNAL(clicked()), this, SLOT(StartCampaign()));
338 connect(ui.pageCampaign->btnPreview, SIGNAL(clicked()), this, SLOT(StartCampaign()));
339 connect(ui.pageCampaign->CBTeam, SIGNAL(currentIndexChanged(int)), this, SLOT(UpdateCampaignPage(int)));
340 connect(ui.pageCampaign->CBTeam, SIGNAL(currentIndexChanged(int)), this, SLOT(UpdateCampaignPageTeam(int)));
341 connect(ui.pageCampaign->CBCampaign, SIGNAL(currentIndexChanged(int)), this, SLOT(UpdateCampaignPage(int)));
342 connect(ui.pageCampaign->CBMission, SIGNAL(currentIndexChanged(int)), this, SLOT(UpdateCampaignPageMission(int)));
343 connect(ui.pageTraining->CBTeam, SIGNAL(currentIndexChanged(int)), this, SLOT(UpdateTrainingPageTeam(int)));
344 connect(ui.pageCampaign->CBTeam, SIGNAL(currentIndexChanged(int)), ui.pageTraining->CBTeam, SLOT(setCurrentIndex(int)));
345 connect(ui.pageTraining->CBTeam, SIGNAL(currentIndexChanged(int)), ui.pageCampaign->CBTeam, SLOT(setCurrentIndex(int)));
346
347 connect(ui.pageSelectWeapon->pWeapons, SIGNAL(weaponsDeleted(QString)),
348 this, SLOT(DeleteWeapons(QString)));
349 connect(ui.pageSelectWeapon->pWeapons, SIGNAL(weaponsAdded(QString, QString)),
350 this, SLOT(AddWeapons(QString, QString)));
351 connect(ui.pageSelectWeapon->pWeapons, SIGNAL(weaponsEdited(QString, QString, QString)),
352 this, SLOT(EditWeapons(QString, QString, QString)));
353 connect(ui.pageSelectWeapon->pWeapons, SIGNAL(weaponsEdited(QString, QString, QString)),
354 ui.pageNetGame->pGameCFG, SLOT(resendAmmoData()));
355
356 connect(ui.pageMain->BtnNetLocal, SIGNAL(clicked()), this, SLOT(GoToNet()));
357 connect(ui.pageMain->BtnNetOfficial, SIGNAL(clicked()), this, SLOT(NetConnectOfficialServer()));
358
359 connect(ui.pageVideos, SIGNAL(goBack()), config, SLOT(SaveVideosOptions()));
360
361 gameSchemeModel = new GameSchemeModel(this, cfgdir->absolutePath() + "/Schemes/Game");
362 ui.pageScheme->setModel(gameSchemeModel);
363 ui.pageMultiplayer->gameCFG->GameSchemes->setModel(gameSchemeModel);
364 ui.pageOptions->SchemesName->setModel(gameSchemeModel);
365
366 wBackground = new BGWidget(this);
367 wBackground->setFixedSize(this->width(), this->height());
368 wBackground->lower();
369 wBackground->init();
370 wBackground->enabled = config->isFrontendEffects();
371 wBackground->startAnimation();
372
373 //Install all eventFilters :
374
375 MouseOverFilter *filter = new MouseOverFilter();
376 filter->setUi(&ui);
377
378 QList<QWidget *> widgets;
379
380 for (int i=0; i < ui.Pages->count(); i++)
381 {
382 widgets = ui.Pages->widget(i)->findChildren<QWidget *>();
383
384 for (int i=0; i < widgets.size(); i++)
385 {
386 widgets.at(i)->installEventFilter(filter);
387 }
388 }
389
390 ui.Pages->setCurrentIndex(ID_PAGE_INFO);
391 PagesStack.push(ID_PAGE_MAIN);
392 ((AbstractPage*)ui.Pages->widget(ID_PAGE_MAIN))->triggerPageEnter();
393 GoBack();
394
395 connect(config, SIGNAL(frontendFullscreen(bool)), this, SLOT(onFrontendFullscreen(bool)));
396 onFrontendFullscreen(config->isFrontendFullscreen());
397 }
398
onFrontendFullscreen(bool value)399 void HWForm::onFrontendFullscreen(bool value)
400 {
401 if (value)
402 setWindowState(windowState() | Qt::WindowFullScreen);
403 else
404 {
405 setWindowState(windowState() & ~Qt::WindowFullScreen);
406 }
407 }
408
onFrontendEffects(bool value)409 void HWForm::onFrontendEffects(bool value)
410 {
411 wBackground->enabled = value;
412 if (value)
413 wBackground->startAnimation();
414 else
415 wBackground->stopAnimation();
416 }
417
onFrontendSoundsToggled(bool value)418 void HWForm::onFrontendSoundsToggled(bool value)
419 {
420 ui.pageEditTeam->frontendSoundsToggled(value);
421 }
422
423 /*
424 void HWForm::keyReleaseEvent(QKeyEvent *event)
425 {
426 if (event->key() == Qt::Key_Escape)
427 this->GoBack();
428 }
429 */
430
CustomizePalettes()431 void HWForm::CustomizePalettes()
432 {
433 // Scroll bar widget palette
434 QList<QScrollBar *> allSBars = findChildren<QScrollBar *>();
435 QPalette pal = palette();
436 pal.setColor(QPalette::WindowText, QColor(0xff, 0xcc, 0x00));
437 pal.setColor(QPalette::Button, QColor(0x00, 0x35, 0x1d));
438 pal.setColor(QPalette::Base, QColor(0x00, 0x35, 0x1d));
439 pal.setColor(QPalette::Window, QColor(0x00, 0x00, 0x00));
440
441 for (int i = 0; i < allSBars.size(); ++i)
442 allSBars.at(i)->setPalette(pal);
443
444 // Set default hyperlink color
445 QPalette appPal = qApp->palette();
446 appPal.setColor(QPalette::Link, QColor(0xff, 0xff, 0x6e));
447 qApp->setPalette(appPal);
448 }
449
UpdateWeapons()450 void HWForm::UpdateWeapons()
451 {
452 QVector<QComboBox*> combos;
453 combos.push_back(ui.pageOptions->WeaponsName);
454 combos.push_back(ui.pageMultiplayer->gameCFG->WeaponsName);
455 combos.push_back(ui.pageNetGame->pGameCFG->WeaponsName);
456 combos.push_back(ui.pageSelectWeapon->selectWeaponSet);
457
458 QStringList names = ui.pageSelectWeapon->pWeapons->getWeaponNames();
459
460 for(QVector<QComboBox*>::iterator it = combos.begin(); it != combos.end(); ++it)
461 {
462 (*it)->clear();
463
464 for(int i = 0; i < names.size(); ++i)
465 (*it)->addItem(names[i], ui.pageSelectWeapon->pWeapons->getWeaponsString(names[i]));
466
467 int pos = (*it)->findText("Default");
468 if (pos != -1)
469 {
470 (*it)->setCurrentIndex(pos);
471 }
472 }
473 }
474
AddWeapons(QString weaponsName,QString ammo)475 void HWForm::AddWeapons(QString weaponsName, QString ammo)
476 {
477 QVector<QComboBox*> combos;
478 combos.push_back(ui.pageOptions->WeaponsName);
479 combos.push_back(ui.pageMultiplayer->gameCFG->WeaponsName);
480 combos.push_back(ui.pageNetGame->pGameCFG->WeaponsName);
481 combos.push_back(ui.pageSelectWeapon->selectWeaponSet);
482
483 QStringList names = ui.pageSelectWeapon->pWeapons->getWeaponNames();
484
485 for(QVector<QComboBox*>::iterator it = combos.begin(); it != combos.end(); ++it)
486 {
487 (*it)->addItem(weaponsName, QVariant(ammo));
488 }
489 ui.pageSelectWeapon->selectWeaponSet->setCurrentIndex(ui.pageSelectWeapon->selectWeaponSet->count()-1);
490 }
491
DeleteWeapons(QString weaponsName)492 void HWForm::DeleteWeapons(QString weaponsName)
493 {
494 QVector<QComboBox*> combos;
495 combos.push_back(ui.pageOptions->WeaponsName);
496 combos.push_back(ui.pageMultiplayer->gameCFG->WeaponsName);
497 combos.push_back(ui.pageNetGame->pGameCFG->WeaponsName);
498 combos.push_back(ui.pageSelectWeapon->selectWeaponSet);
499
500 QStringList names = ui.pageSelectWeapon->pWeapons->getWeaponNames();
501
502 for(QVector<QComboBox*>::iterator it = combos.begin(); it != combos.end(); ++it)
503 {
504 int pos = (*it)->findText(weaponsName);
505 if (pos != -1)
506 {
507 (*it)->removeItem(pos);
508 }
509 }
510 ui.pageSelectWeapon->pWeapons->deletionDone();
511 }
512
EditWeapons(QString oldWeaponsName,QString newWeaponsName,QString ammo)513 void HWForm::EditWeapons(QString oldWeaponsName, QString newWeaponsName, QString ammo)
514 {
515 QVector<QComboBox*> combos;
516 combos.push_back(ui.pageOptions->WeaponsName);
517 combos.push_back(ui.pageMultiplayer->gameCFG->WeaponsName);
518 combos.push_back(ui.pageNetGame->pGameCFG->WeaponsName);
519 combos.push_back(ui.pageSelectWeapon->selectWeaponSet);
520
521 QStringList names = ui.pageSelectWeapon->pWeapons->getWeaponNames();
522
523 for(QVector<QComboBox*>::iterator it = combos.begin(); it != combos.end(); ++it)
524 {
525 int pos = (*it)->findText(oldWeaponsName);
526 (*it)->setItemText(pos, newWeaponsName);
527 (*it)->setItemData(pos, ammo);
528 }
529 }
530
UpdateTeamsLists()531 void HWForm::UpdateTeamsLists()
532 {
533 QStringList teamslist = config->GetTeamsList();
534
535 if(teamslist.empty())
536 {
537 QString currentNickName = config->value("net/nick", config->getRandomNick()).toString();
538 QString teamName;
539 int firstHumanTeam = 1;
540 int lastHumanTeam = 2;
541
542 // Default team
543 if (currentNickName.isEmpty())
544 {
545 teamName = tr("Team 1");
546 firstHumanTeam++;
547 }
548 else
549 {
550 teamName = tr("%1's Team").arg(currentNickName);
551 lastHumanTeam--;
552 }
553
554 HWTeam defaultTeam(teamName);
555 // Randomize fort and grave for greater variety by default.
556 // But we exclude DLC graves and forts to not have desyncing teams by default
557 // TODO: Remove DLC filtering when it isn't neccessary anymore
558 HWNamegen::teamRandomGrave(defaultTeam, false);
559 HWNamegen::teamRandomFort(defaultTeam, false);
560 HWNamegen::teamLocalizedDefaultVoice(defaultTeam);
561 defaultTeam.saveToFile();
562 teamslist.push_back(teamName);
563
564 // Add additional default teams
565
566 // More human teams to allow local multiplayer instantly
567 for(int i=firstHumanTeam; i<=lastHumanTeam; i++)
568 {
569 //: Default team name
570 teamName = tr("Team %1").arg(i);
571 HWTeam numberTeam(teamName);
572 HWNamegen::teamRandomGrave(numberTeam, false);
573 HWNamegen::teamRandomFort(numberTeam, false);
574 HWNamegen::teamLocalizedDefaultVoice(numberTeam);
575 numberTeam.saveToFile();
576 teamslist.push_back(teamName);
577 }
578 // Add 2 default CPU teams
579 for(int i=1; i<=5; i=i+2)
580 {
581 //: Default computer team name
582 teamName = tr("Computer %1").arg(i);
583 HWTeam numberTeam(teamName);
584 HWNamegen::teamRandomGrave(numberTeam, false);
585 HWNamegen::teamRandomFort(numberTeam, false);
586 HWNamegen::teamLocalizedDefaultVoice(numberTeam);
587 numberTeam.setDifficulty(6-i);
588 numberTeam.saveToFile();
589 teamslist.push_back(teamName);
590 }
591 }
592
593 ui.pageOptions->CBTeamName->clear();
594 ui.pageOptions->CBTeamName->addItems(teamslist);
595 ui.pageCampaign->CBTeam->clear();
596 ui.pageTraining->CBTeam->clear();
597 /* Only show human teams in campaign/training page */
598 bool playable = false;
599 for(int i=0; i<teamslist.length(); i++)
600 {
601 HWTeam testTeam = HWTeam(teamslist[i]);
602 testTeam.loadFromFile();
603 if(testTeam.difficulty() == 0)
604 {
605 ui.pageCampaign->CBTeam->addItem(teamslist[i]);
606 ui.pageTraining->CBTeam->addItem(teamslist[i]);
607 playable = true;
608 }
609 }
610 ui.pageCampaign->BtnStartCampaign->setEnabled(playable);
611 ui.pageCampaign->btnPreview->setEnabled(playable);
612 ui.pageTraining->btnStart->setEnabled(playable);
613 ui.pageTraining->btnPreview->setEnabled(playable);
614 UpdateTrainingPageTeam(0);
615 }
616
GoToNewWeapons()617 void HWForm::GoToNewWeapons()
618 {
619 ui.pageSelectWeapon->pWeapons->newWeaponsName();
620 GoToPage(ID_PAGE_SELECTWEAPON);
621 }
622
GoToEditWeapons()623 void HWForm::GoToEditWeapons()
624 {
625 ui.pageSelectWeapon->selectWeaponSet->setCurrentIndex(ui.pageOptions->WeaponsName->currentIndex());
626 GoToPage(ID_PAGE_SELECTWEAPON);
627 }
628
GoToWeapons(int index)629 void HWForm::GoToWeapons(int index)
630 {
631 ui.pageSelectWeapon->selectWeaponSet->setCurrentIndex(index);
632 GoToPage(ID_PAGE_SELECTWEAPON);
633 }
634
635
GoToSaves()636 void HWForm::GoToSaves()
637 {
638 ui.pagePlayDemo->FillFromDir(PagePlayDemo::RT_Save);
639
640 GoToPage(ID_PAGE_DEMOS);
641 }
642
GoToDemos()643 void HWForm::GoToDemos()
644 {
645 ui.pagePlayDemo->FillFromDir(PagePlayDemo::RT_Demo);
646
647 GoToPage(ID_PAGE_DEMOS);
648 }
649
GoToNet()650 void HWForm::GoToNet()
651 {
652 ui.pageNet->updateServersList();
653
654 GoToPage(ID_PAGE_NET);
655 }
656
GoToScheme(int index)657 void HWForm::GoToScheme(int index)
658 {
659 ui.pageScheme->selectScheme->setCurrentIndex(index);
660 GoToPage(ID_PAGE_SCHEME);
661 }
662
GoToNewScheme()663 void HWForm::GoToNewScheme()
664 {
665 ui.pageScheme->newRow();
666 GoToPage(ID_PAGE_SCHEME);
667 }
668
GoToEditScheme()669 void HWForm::GoToEditScheme()
670 {
671 ui.pageScheme->selectScheme->setCurrentIndex(ui.pageOptions->SchemesName->currentIndex());
672 GoToPage(ID_PAGE_SCHEME);
673 }
674
GoToHelp()675 void HWForm::GoToHelp()
676 {
677 // For now just opens the Hedgewars Wiki in external browser.
678 // TODO: Replace this with an offline help someday (bug 660).
679 QDesktopServices::openUrl(QUrl("https://hedgewars.org/wiki"));
680 }
681
GoToVideos()682 void HWForm::GoToVideos()
683 {
684 GoToPage(ID_PAGE_VIDEOS);
685 }
686
GoToTraining()687 void HWForm::GoToTraining()
688 {
689 GoToPage(ID_PAGE_TRAINING);
690 }
691
692 //TODO: maybe find a better place for this?
stringifyPageId(quint32 id)693 QString HWForm::stringifyPageId(quint32 id)
694 {
695 QString pageName;
696 switch (id)
697 {
698 case ID_PAGE_SETUP_TEAM : pageName = "PAGE_SETUP_TEAM"; break;
699 case ID_PAGE_SETUP : pageName = "PAGE_SETUP"; break;
700 case ID_PAGE_MULTIPLAYER : pageName = "PAGE_MULTIPLAYER"; break;
701 case ID_PAGE_DEMOS : pageName = "PAGE_DEMOS"; break;
702 case ID_PAGE_NET : pageName = "PAGE_NET"; break;
703 case ID_PAGE_NETGAME : pageName = "PAGE_NETGAME"; break;
704 case ID_PAGE_INFO : pageName = "PAGE_INFO"; break;
705 case ID_PAGE_MAIN : pageName = "PAGE_MAIN"; break;
706 case ID_PAGE_GAMESTATS : pageName = "PAGE_GAMESTATS"; break;
707 case ID_PAGE_SINGLEPLAYER : pageName = "PAGE_SINGLEPLAYER"; break;
708 case ID_PAGE_TRAINING : pageName = "PAGE_TRAINING"; break;
709 case ID_PAGE_SELECTWEAPON : pageName = "PAGE_SELECTWEAPON"; break;
710 case ID_PAGE_NETSERVER : pageName = "PAGE_NETSERVER"; break;
711 case ID_PAGE_INGAME : pageName = "PAGE_INGAME"; break;
712 case ID_PAGE_ROOMSLIST : pageName = "PAGE_ROOMSLIST"; break;
713 case ID_PAGE_CONNECTING : pageName = "PAGE_CONNECTING"; break;
714 case ID_PAGE_SCHEME : pageName = "PAGE_SCHEME"; break;
715 case ID_PAGE_ADMIN : pageName = "PAGE_ADMIN"; break;
716 case ID_PAGE_CAMPAIGN : pageName = "PAGE_CAMPAIGN"; break;
717 case ID_PAGE_DRAWMAP : pageName = "PAGE_DRAWMAP"; break;
718 case ID_PAGE_DATADOWNLOAD : pageName = "PAGE_DATADOWNLOAD"; break;
719 case ID_PAGE_VIDEOS : pageName = "PAGE_VIDEOS"; break;
720 case MAX_PAGE : pageName = "MAX_PAGE"; break;
721 default : pageName = "UNKNOWN_PAGE"; break;
722 }
723 return pageName;
724 }
725
OnPageShown(quint8 id,quint8 lastid)726 void HWForm::OnPageShown(quint8 id, quint8 lastid)
727 {
728 #ifdef QT_DEBUG
729 qDebug("Leaving %s, entering %s", qPrintable(stringifyPageId(lastid)), qPrintable(stringifyPageId(id)));
730 #endif
731 if (lastid == ID_PAGE_MAIN)
732 {
733 ui.pageMain->resetNetworkChoice();
734 }
735
736 // pageEnter and pageLeave events
737 ((AbstractPage*)ui.Pages->widget(lastid))->triggerPageLeave();
738 ((AbstractPage*)ui.Pages->widget(id))->triggerPageEnter();
739
740 if (id == ID_PAGE_DATADOWNLOAD)
741 {
742 ui.pageDataDownload->fetchList();
743 }
744 if (id == ID_PAGE_DRAWMAP)
745 {
746 DrawMapScene * scene;
747 if(lastid == ID_PAGE_MULTIPLAYER)
748 scene = ui.pageMultiplayer->gameCFG->pMapContainer->getDrawMapScene();
749 else
750 scene = ui.pageNetGame->pGameCFG->pMapContainer->getDrawMapScene();
751
752 ui.pageDrawMap->drawMapWidget->setScene(scene);
753 }
754
755 if (lastid == ID_PAGE_DRAWMAP)
756 {
757 if (id == ID_PAGE_MULTIPLAYER)
758 ui.pageMultiplayer->gameCFG->pMapContainer->mapDrawingFinished();
759 else
760 ui.pageNetGame->pGameCFG->pMapContainer->mapDrawingFinished();
761 }
762
763 if (id == ID_PAGE_ROOMSLIST)
764 {
765 if (hwnet && game && game->gameState == gsStarted) // abnormal exit - kick or room destruction - send kills.
766 {
767 game->netSuspend = true;
768 ui.pageRoomsList->displayWarning(tr("Game aborted"));
769 game->abort();
770 }
771 }
772
773 if (id == ID_PAGE_MULTIPLAYER || id == ID_PAGE_NETGAME)
774 {
775 QStringList tmNames = config->GetTeamsList();
776 TeamSelWidget* curTeamSelWidget;
777 ui.pageOptions->setTeamOptionsEnabled(false);
778
779 if (id == ID_PAGE_MULTIPLAYER)
780 {
781 curTeamSelWidget = ui.pageMultiplayer->teamsSelect;
782 }
783 else
784 {
785 curTeamSelWidget = ui.pageNetGame->pNetTeamsWidget;
786 }
787
788 QList<HWTeam> teamsList;
789 for (QStringList::iterator it = tmNames.begin(); it != tmNames.end(); ++it)
790 {
791 HWTeam team(*it);
792 team.loadFromFile();
793 teamsList.push_back(team);
794 }
795
796 if (lastid == ID_PAGE_SETUP || lastid == ID_PAGE_DRAWMAP) // _TEAM
797 {
798 if (editedTeam)
799 {
800 curTeamSelWidget->addTeam(*editedTeam);
801 }
802 }
803 else if (lastid != ID_PAGE_GAMESTATS
804 && lastid != ID_PAGE_INGAME
805 && lastid != ID_PAGE_SCHEME
806 && lastid != ID_PAGE_SELECTWEAPON)
807 {
808 curTeamSelWidget->resetPlayingTeams(teamsList);
809 }
810 }
811
812 if (id == ID_PAGE_GAMESTATS)
813 {
814 switch(lastGameType) {
815 case gtLocal:
816 case gtQLocal:
817 case gtTraining:
818 case gtCampaign:
819 case gtDemo:
820 case gtSave:
821 ui.pageGameStats->restartBtnVisible(true);
822 break;
823 default:
824 ui.pageGameStats->restartBtnVisible(false);
825 break;
826 }
827 ui.pageGameStats->saveDemoBtnEnabled(demoIsPresent);
828 }
829
830 if (id == ID_PAGE_MAIN)
831 {
832 ui.pageOptions->setTeamOptionsEnabled(true);
833 }
834 }
835
GoToPage(int id)836 void HWForm::GoToPage(int id)
837 {
838 //bool stopAnim = false;
839
840 int lastid = ui.Pages->currentIndex();
841 PagesStack.push(ui.Pages->currentIndex());
842
843 OnPageShown(id, lastid);
844 ui.Pages->setCurrentIndex(id);
845
846
847 /* if (id == ID_PAGE_DRAWMAP || id == ID_PAGE_GAMESTATS)
848 stopAnim = true;
849 This were disabled due to broken flake animations. I believe the more general problems w/ opacity that forced its disable makes blocking these
850 unnecessary.
851 */
852
853
854 #if (QT_VERSION >= 0x040600)
855 //if (!stopAnim)
856 {
857 /**Start animation :**/
858 int coeff = 1;
859 #ifdef false
860 coeff = 2;
861 QGraphicsOpacityEffect *effectNew = new QGraphicsOpacityEffect(ui.Pages->widget(id));
862 ui.Pages->widget(id)->setGraphicsEffect(effectNew);
863
864 QGraphicsOpacityEffect *effectLast = new QGraphicsOpacityEffect(ui.Pages->widget(lastid));
865 ui.Pages->widget(lastid)->setGraphicsEffect(effectLast);
866 #endif
867 // no effects, means 0 effect duration :D
868 int duration = config->isFrontendEffects() ? 500 : 0;
869
870 //New page animation
871 animationNewSlide = new QPropertyAnimation(ui.Pages->widget(id), "pos");
872 animationNewSlide->setDuration(duration);
873 animationNewSlide->setStartValue(QPoint(width()/coeff, 0));
874 animationNewSlide->setEndValue(QPoint(0, 0));
875 animationNewSlide->setEasingCurve(QEasingCurve::OutExpo);
876
877 #ifdef false
878 animationNewOpacity = new QPropertyAnimation(effectNew, "opacity");
879 animationNewOpacity->setDuration(duration);
880 animationNewOpacity->setStartValue(0.01);
881 animationNewOpacity->setEndValue(1);
882 animationNewOpacity->setEasingCurve(QEasingCurve::OutExpo);
883 #endif
884
885 //Last page animation
886 ui.Pages->widget(lastid)->setHidden(false);
887
888 animationOldSlide = new QPropertyAnimation(ui.Pages->widget(lastid), "pos");
889 animationOldSlide->setDuration(duration);
890 animationOldSlide->setStartValue(QPoint(0, 0));
891 animationOldSlide->setEndValue(QPoint(-width()/coeff, 0));
892 animationOldSlide->setEasingCurve(QEasingCurve::OutExpo);
893
894 #ifdef false
895 animationOldOpacity = new QPropertyAnimation(effectLast, "opacity");
896 animationOldOpacity->setDuration(duration);
897 animationOldOpacity->setStartValue(1);
898 animationOldOpacity->setEndValue(0.01);
899 animationOldOpacity->setEasingCurve(QEasingCurve::OutExpo);
900 #endif
901
902 // let's hide the old slide after its animation has finished
903 connect(animationOldSlide, SIGNAL(finished()), ui.Pages->widget(lastid), SLOT(hide()));
904
905 // start animations
906 animationOldSlide->start(QAbstractAnimation::DeleteWhenStopped);
907 animationNewSlide->start(QAbstractAnimation::DeleteWhenStopped);
908
909 /* this is for the situation when the animation below is interrupted by a new animation. For some reason, finished is not being fired */
910 for(int i=0;i<MAX_PAGE;i++) if (i!=id && i!=lastid) ui.Pages->widget(i)->hide();
911 }
912 #endif
913 }
914
GoBack()915 void HWForm::GoBack()
916 {
917 bool stopAnim = false;
918 int curid = ui.Pages->currentIndex();
919 if (curid == ID_PAGE_MAIN)
920 {
921 ((AbstractPage*)ui.Pages->widget(ID_PAGE_MAIN))->triggerPageLeave();
922 if (!ui.pageVideos->tryQuit(this))
923 return;
924 stopAnim = true;
925 exit();
926 }
927
928 int id = PagesStack.isEmpty() ? ID_PAGE_MAIN : PagesStack.pop();
929 ui.Pages->setCurrentIndex(id);
930 OnPageShown(id, curid);
931
932 if (id == ID_PAGE_CONNECTING)
933 {
934 stopAnim = true;
935 GoBack();
936 }
937 if (id == ID_PAGE_NETSERVER)
938 {
939 stopAnim = true;
940 GoBack();
941 }
942 if ((!hwnet) && (id == ID_PAGE_ROOMSLIST))
943 {
944 stopAnim = true;
945 GoBack();
946 }
947 /*if (curid == ID_PAGE_DRAWMAP)
948 stopAnim = true; */
949
950 if ((!hwnet) || (!hwnet->isInRoom()))
951 if (id == ID_PAGE_NETGAME)
952 {
953 stopAnim = true;
954 GoBack();
955 }
956
957 if (curid == ID_PAGE_CAMPAIGN)
958 config->setValue("frontend/lastSingleplayerTeam", ui.pageCampaign->CBTeam->currentText());
959 if (curid == ID_PAGE_TRAINING)
960 config->setValue("frontend/lastSingleplayerTeam", ui.pageTraining->CBTeam->currentText());
961
962 if (curid == ID_PAGE_ROOMSLIST || curid == ID_PAGE_CONNECTING) NetDisconnect();
963 if (curid == ID_PAGE_NETGAME && hwnet && hwnet->isInRoom()) hwnet->partRoom();
964 // need to work on this, can cause invalid state for admin quit trying to prevent bad state message on kick
965 //if (curid == ID_PAGE_NETGAME && (!game || game->gameState != gsStarted)) hwnet->partRoom();
966
967 if (curid == ID_PAGE_SCHEME)
968 gameSchemeModel->Save();
969
970 #if (QT_VERSION >= 0x040600)
971 /**Start animation :**/
972 if (curid != 0 && !stopAnim)
973 {
974 int coeff = 1;
975 #ifdef false
976 coeff = 2;
977 QGraphicsOpacityEffect *effectNew = new QGraphicsOpacityEffect(ui.Pages->widget(id));
978 effectNew->setOpacity(1);
979 ui.Pages->widget(id)->setGraphicsEffect(effectNew);
980
981 QGraphicsOpacityEffect *effectLast = new QGraphicsOpacityEffect(ui.Pages->widget(curid));
982 ui.Pages->widget(curid)->setGraphicsEffect(effectLast);
983 #endif
984 // no effects, means 0 effect duration :D
985 int duration = config->isFrontendEffects() ? 500 : 0;
986
987 //Last page animation
988 animationOldSlide = new QPropertyAnimation(ui.Pages->widget(id), "pos");
989 animationOldSlide->setDuration(duration);
990 animationOldSlide->setStartValue(QPoint(-width()/coeff, 0));
991 animationOldSlide->setEndValue(QPoint(0, 0));
992 animationOldSlide->setEasingCurve(QEasingCurve::OutExpo);
993
994 #ifdef false
995 animationOldOpacity = new QPropertyAnimation(effectLast, "opacity");
996 animationOldOpacity->setDuration(duration);
997 animationOldOpacity->setStartValue(1);
998 animationOldOpacity->setEndValue(0.01);
999 animationOldOpacity->setEasingCurve(QEasingCurve::OutExpo);
1000 #endif
1001 //New page animation
1002 ui.Pages->widget(curid)->setHidden(false);
1003
1004 animationNewSlide = new QPropertyAnimation(ui.Pages->widget(curid), "pos");
1005 animationNewSlide->setDuration(duration);
1006 animationNewSlide->setStartValue(QPoint(0, 0));
1007 animationNewSlide->setEndValue(QPoint(width()/coeff, 0));
1008 animationNewSlide->setEasingCurve(QEasingCurve::OutExpo);
1009
1010 #ifdef false
1011 animationNewOpacity = new QPropertyAnimation(effectNew, "opacity");
1012 animationNewOpacity->setDuration(duration);
1013 animationNewOpacity->setStartValue(0.01);
1014 animationNewOpacity->setEndValue(1);
1015 animationNewOpacity->setEasingCurve(QEasingCurve::OutExpo);
1016 #endif
1017
1018 // let's hide the old slide after its animation has finished
1019 connect(animationNewSlide, SIGNAL(finished()), ui.Pages->widget(curid), SLOT(hide()));
1020
1021 // start animations
1022 animationOldSlide->start(QAbstractAnimation::DeleteWhenStopped);
1023 animationNewSlide->start(QAbstractAnimation::DeleteWhenStopped);
1024 }
1025 #endif
1026
1027 if (stopAnim)
1028 ui.Pages->widget(curid)->hide();
1029
1030 // TODO the whole pages shown and effects stuff should be moved
1031 // out of hwform.cpp and into a subclass of QStackedLayout
1032
1033 }
1034
OpenSnapshotFolder()1035 void HWForm::OpenSnapshotFolder()
1036 {
1037 QString path = QDir::toNativeSeparators(cfgdir->absolutePath() + "/Screenshots");
1038 QDesktopServices::openUrl(QUrl("file:///" + path));
1039 }
1040
btnExitPressed()1041 void HWForm::btnExitPressed()
1042 {
1043 eggTimer.start();
1044 }
1045
exit()1046 void HWForm::exit()
1047 {
1048 // if (eggTimer.elapsed() < 3000){
1049 #ifdef __APPLE__
1050 panel->showInstallController();
1051 #endif
1052 close();
1053 // TODO reactivate egg
1054 /* }
1055 else
1056 {
1057 QPushButton * btn = findChild<QPushButton *>("imageButt");
1058 if (btn)
1059 {
1060 btn->setIcon(QIcon(":/res/bonus.png"));
1061 }
1062 } */
1063 }
1064
IntermediateSetup()1065 void HWForm::IntermediateSetup()
1066 {
1067 quint8 id=ui.Pages->currentIndex();
1068 TeamSelWidget* curTeamSelWidget;
1069
1070 if(id == ID_PAGE_MULTIPLAYER)
1071 {
1072 curTeamSelWidget = ui.pageMultiplayer->teamsSelect;
1073 }
1074 else
1075 {
1076 curTeamSelWidget = ui.pageNetGame->pNetTeamsWidget;
1077 }
1078
1079 QStringList tmnames;
1080
1081 foreach(HWTeam team, curTeamSelWidget->getNotPlayingTeams())
1082 tmnames += team.name();
1083
1084 //UpdateTeamsLists(&tmnames); // FIXME: still need more work if teamname is updated while configuring
1085 UpdateTeamsLists();
1086
1087 GoToPage(ID_PAGE_SETUP);
1088 }
1089
NewTeam()1090 void HWForm::NewTeam()
1091 {
1092 QString teamName = QLineEdit::tr("unnamed");
1093 QStringList teamslist = config->GetTeamsList();
1094 if(teamslist.contains(teamName))
1095 {
1096 //name already used -> look for an appropriate name:
1097 int i=2;
1098 while(teamslist.contains(teamName = QLineEdit::tr("unnamed (%1)").arg(i++)));
1099 }
1100
1101 ui.pageEditTeam->createTeam(teamName, playerHash);
1102 UpdateTeamsLists();
1103 GoToPage(ID_PAGE_SETUP_TEAM);
1104 }
1105
EditTeam(const QString & teamName)1106 void HWForm::EditTeam(const QString & teamName)
1107 {
1108 ui.pageEditTeam->editTeam(teamName, playerHash);
1109 GoToPage(ID_PAGE_SETUP_TEAM);
1110 }
1111
AfterTeamEdit()1112 void HWForm::AfterTeamEdit()
1113 {
1114 UpdateTeamsLists();
1115 //GoBack();
1116 }
1117
1118
DeleteTeam(const QString & teamName)1119 void HWForm::DeleteTeam(const QString & teamName)
1120 {
1121 ui.pageEditTeam->deleteTeam(teamName);
1122 UpdateTeamsLists();
1123 }
1124
DeleteScheme()1125 void HWForm::DeleteScheme()
1126 {
1127 ui.pageScheme->selectScheme->setCurrentIndex(ui.pageOptions->SchemesName->currentIndex());
1128 if (ui.pageOptions->SchemesName->currentIndex() < gameSchemeModel->numberOfDefaultSchemes)
1129 {
1130 MessageDialog::ShowErrorMessage(QMessageBox::tr("Cannot delete default scheme '%1'!").arg(ui.pageOptions->SchemesName->currentText()), this);
1131 }
1132 else
1133 {
1134 ui.pageScheme->deleteRow();
1135 gameSchemeModel->Save();
1136 }
1137 }
1138
DeleteWeaponSet()1139 void HWForm::DeleteWeaponSet()
1140 {
1141 ui.pageSelectWeapon->selectWeaponSet->setCurrentIndex(ui.pageOptions->WeaponsName->currentIndex());
1142 ui.pageSelectWeapon->pWeapons->deleteWeaponsName();
1143 }
1144
SimpleGame()1145 void HWForm::SimpleGame()
1146 {
1147 CreateGame(0, 0, *cDefaultAmmoStore);
1148 game->StartQuick();
1149 }
1150
PlayDemo()1151 void HWForm::PlayDemo()
1152 {
1153 QListWidgetItem * curritem = ui.pagePlayDemo->DemosList->currentItem();
1154 if (!curritem)
1155 {
1156 MessageDialog::ShowErrorMessage(QMessageBox::tr("Please select a record from the list"), this);
1157 return;
1158 }
1159 CreateGame(0, 0, 0);
1160 game->PlayDemo(curritem->data(Qt::UserRole).toString(), ui.pagePlayDemo->isSave());
1161 }
1162
PlayDemoQuick(const QString & demofilename)1163 void HWForm::PlayDemoQuick(const QString & demofilename)
1164 {
1165 GoToPage(ID_PAGE_MAIN);
1166 //GoBack() <- don't or you'll close the socket
1167 CreateGame(0, 0, 0);
1168 game->PlayDemo(demofilename, false);
1169 }
1170
NetConnectQuick(const QString & host,quint16 port)1171 void HWForm::NetConnectQuick(const QString & host, quint16 port)
1172 {
1173 GoToPage(ID_PAGE_MAIN);
1174 NetConnectServer(host, port, false);
1175 }
1176
NetConnectServer(const QString & host,quint16 port,bool useTls)1177 void HWForm::NetConnectServer(const QString & host, quint16 port, bool useTls)
1178 {
1179 qDebug("connecting to %s:%d", qPrintable(host), port);
1180 _NetConnect(host, port, useTls, ui.pageOptions->editNetNick->text().trimmed());
1181 }
1182
NetConnectOfficialServer()1183 void HWForm::NetConnectOfficialServer()
1184 {
1185 NetConnectServer(NETGAME_DEFAULT_SERVER, NETGAME_DEFAULT_PORT, false);
1186 }
1187
NetPassword(const QString & nick)1188 void HWForm::NetPassword(const QString & nick)
1189 {
1190 Q_UNUSED(nick);
1191 //Get hashes
1192 QString hash = config->passwordHash();
1193 QString temphash = config->tempHash();
1194
1195 //Check them
1196
1197 if (temphash.isEmpty() && hash.isEmpty()) { //If the user enters a registered nick with no password, sends a bogus hash
1198 hwnet->SendPasswordHash("THISISNOHASH");
1199 }
1200 else if (temphash.isEmpty()) { //Send saved hash as default
1201 hwnet->SendPasswordHash(hash);
1202 }
1203 else { //Send the hash
1204 hwnet->SendPasswordHash(temphash);
1205 }
1206
1207 //Remove temporary hash from config
1208 config->clearTempHash();
1209 }
1210
NetNickRegistered(const QString & nick)1211 void HWForm::NetNickRegistered(const QString & nick)
1212 {
1213 //Get hashes
1214 QString hash = config->passwordHash();
1215 QString temphash = config->tempHash();
1216
1217 if (hash.isEmpty()) {
1218 if (temphash.isEmpty()) { //If the user enters a registered nick with no password
1219 QString suppliedpass;
1220 while (suppliedpass.isEmpty()) {
1221 QInputDialog nickRegedDialog(this);
1222 nickRegedDialog.setWindowModality(Qt::WindowModal);
1223 nickRegedDialog.setInputMode(QInputDialog::TextInput);
1224 nickRegedDialog.setWindowTitle(tr("Hedgewars - Nick registered"));
1225 nickRegedDialog.setLabelText(tr("This nick is registered, and you haven't specified a password.\n\nIf this nick isn't yours, please register your own nick at www.hedgewars.org\n\nPassword:"));
1226 nickRegedDialog.setTextEchoMode(QLineEdit::Password);
1227 nickRegedDialog.exec();
1228
1229 suppliedpass = nickRegedDialog.textValue();
1230
1231 if (nickRegedDialog.result() == QDialog::Rejected) {
1232 config->clearPasswordHash();
1233 config->clearTempHash();
1234 GoBack();
1235 return;
1236 }
1237 temphash = QCryptographicHash::hash(suppliedpass.toUtf8(), QCryptographicHash::Md5).toHex();
1238 config->setTempHash(temphash);
1239 }
1240 }
1241 }
1242 NetPassword(nick);
1243 }
1244
NetNickNotRegistered(const QString & nick)1245 void HWForm::NetNickNotRegistered(const QString & nick)
1246 {
1247 Q_UNUSED(nick);
1248
1249 QMessageBox noRegMsg(this);
1250 noRegMsg.setIcon(QMessageBox::Information);
1251 noRegMsg.setWindowTitle(QMessageBox::tr("Hedgewars - Nick not registered"));
1252 noRegMsg.setWindowModality(Qt::WindowModal);
1253 noRegMsg.setText(tr("Your nickname is not registered.\nTo prevent someone else from using it,\nplease register it at www.hedgewars.org"));
1254
1255 if (!config->passwordHash().isEmpty())
1256 {
1257 config->clearPasswordHash();
1258 noRegMsg.setText(noRegMsg.text()+tr("\n\nYour password wasn't saved either."));
1259 }
1260 if (!config->tempHash().isEmpty())
1261 {
1262 config->clearTempHash();
1263 }
1264 noRegMsg.exec();
1265 }
1266
NetNickTaken(const QString & nick)1267 void HWForm::NetNickTaken(const QString & nick)
1268 {
1269 bool ok = false;
1270 QString newNick = QInputDialog::getText(this, tr("Nickname"), tr("Someone already uses your nickname %1 on the server.\nPlease pick another nickname:").arg(nick), QLineEdit::Normal, nick, &ok);
1271
1272 if (!ok || newNick.isEmpty())
1273 {
1274 //ForcedDisconnect(tr("No nickname supplied."));
1275 bool retry = RetryDialog(tr("Hedgewars - Empty nickname"), tr("No nickname supplied."));
1276 GoBack();
1277 if (retry && hwnet) {
1278 if (hwnet->m_private_game) {
1279 QStringList list = hwnet->getHost().split(":");
1280 NetConnectServer(list.at(0), list.at(1).toShort(), false);
1281 } else
1282 NetConnectOfficialServer();
1283 }
1284 return;
1285 }
1286
1287 if(hwnet)
1288 hwnet->NewNick(newNick);
1289 config->setValue("net/nick", newNick);
1290 config->updNetNick();
1291
1292 ui.pageRoomsList->setUser(nick);
1293 ui.pageNetGame->setUser(nick);
1294 }
1295
NetAuthFailed()1296 void HWForm::NetAuthFailed()
1297 {
1298 // Set the password blank if case the user tries to join and enter his password again
1299 config->clearTempHash();
1300
1301 //Try to login again
1302 bool retry = RetryDialog(tr("Hedgewars - Wrong password"), tr("You entered a wrong password."));
1303 GoBack();
1304
1305 config->clearPasswordHash();
1306 config->clearTempHash();
1307 if (retry) {
1308 NetConnectOfficialServer();
1309 }
1310 }
1311
askRoomPassword()1312 void HWForm::askRoomPassword()
1313 {
1314 QString password = QInputDialog::getText(this, tr("Room password"), tr("The room is protected with password.\nPlease, enter the password:"));
1315 if(hwnet && !password.isEmpty())
1316 hwnet->roomPasswordEntered(password);
1317 }
1318
RetryDialog(const QString & title,const QString & label)1319 bool HWForm::RetryDialog(const QString & title, const QString & label)
1320 {
1321 QMessageBox retryMsg(this);
1322 retryMsg.setIcon(QMessageBox::Warning);
1323 retryMsg.setWindowTitle(title);
1324 retryMsg.setText(label);
1325 retryMsg.setWindowModality(Qt::WindowModal);
1326
1327 retryMsg.addButton(QMessageBox::Cancel);
1328
1329 QPushButton *retryButton = retryMsg.addButton(QMessageBox::Ok);
1330 retryButton->setText(tr("Try Again"));
1331 retryButton->setFocus();
1332
1333 retryMsg.exec();
1334
1335 if (retryMsg.clickedButton() == retryButton) {
1336 return true;
1337 }
1338 return false;
1339 }
1340
NetTeamAccepted(const QString & team)1341 void HWForm::NetTeamAccepted(const QString & team)
1342 {
1343 ui.pageNetGame->pNetTeamsWidget->changeTeamStatus(team);
1344 }
1345
NetError(const QString & errmsg)1346 void HWForm::NetError(const QString & errmsg)
1347 {
1348 switch (ui.Pages->currentIndex())
1349 {
1350 case ID_PAGE_INGAME:
1351 MessageDialog::ShowErrorMessage(errmsg, this);
1352 /* fallthrough */
1353 case ID_PAGE_NETGAME:
1354 ui.pageNetGame->displayError(errmsg);
1355 break;
1356 default:
1357 ui.pageRoomsList->displayError(errmsg);
1358 }
1359 }
1360
NetWarning(const QString & wrnmsg)1361 void HWForm::NetWarning(const QString & wrnmsg)
1362 {
1363 if (ui.Pages->currentIndex() == ID_PAGE_NETGAME || ui.Pages->currentIndex() == ID_PAGE_INGAME)
1364 ui.pageNetGame->displayWarning(wrnmsg);
1365 else
1366 ui.pageRoomsList->displayWarning(wrnmsg);
1367 }
1368
_NetConnect(const QString & hostName,quint16 port,bool useTls,QString nick)1369 void HWForm::_NetConnect(const QString & hostName, quint16 port, bool useTls, QString nick)
1370 {
1371 Q_UNUSED(nick);
1372
1373 if (hwnet) {
1374 // destroy old connection
1375 hwnet->Disconnect();
1376 delete hwnet;
1377 hwnet = NULL;
1378 }
1379
1380 hwnet = new HWNewNet();
1381
1382 GoToPage(ID_PAGE_CONNECTING);
1383
1384 connect(hwnet, SIGNAL(AskForRunGame()), this, SLOT(CreateNetGame()), Qt::QueuedConnection);
1385 connect(hwnet, SIGNAL(AskForOfficialServerDemo()), this, SLOT(PlayOfficialServerDemo()), Qt::QueuedConnection);
1386 connect(hwnet, SIGNAL(redirected(quint16)), this, SLOT(NetRedirected(quint16)), Qt::QueuedConnection);
1387 connect(hwnet, SIGNAL(connected()), this, SLOT(NetConnected()), Qt::QueuedConnection);
1388 connect(hwnet, SIGNAL(Error(const QString&)), this, SLOT(NetError(const QString&)), Qt::QueuedConnection);
1389 connect(hwnet, SIGNAL(Warning(const QString&)), this, SLOT(NetWarning(const QString&)), Qt::QueuedConnection);
1390 connect(hwnet, SIGNAL(EnteredGame()), this, SLOT(NetGameEnter()), Qt::QueuedConnection);
1391 connect(hwnet, SIGNAL(LeftRoom(const QString&)), this, SLOT(NetLeftRoom(const QString&)), Qt::QueuedConnection);
1392 connect(hwnet, SIGNAL(AddNetTeam(const HWTeam&)), this, SLOT(AddNetTeam(const HWTeam&)), Qt::QueuedConnection);
1393 connect(hwnet, SIGNAL(RemoveNetTeam(const HWTeam&)), this, SLOT(RemoveNetTeam(const HWTeam&)), Qt::QueuedConnection);
1394 connect(hwnet, SIGNAL(TeamAccepted(const QString&)), this, SLOT(NetTeamAccepted(const QString&)), Qt::QueuedConnection);
1395 connect(hwnet, SIGNAL(NickRegistered(const QString&)), this, SLOT(NetNickRegistered(const QString&)), Qt::QueuedConnection);
1396 connect(hwnet, SIGNAL(NickNotRegistered(const QString&)), this, SLOT(NetNickNotRegistered(const QString&)), Qt::QueuedConnection);
1397 connect(hwnet, SIGNAL(NickTaken(const QString&)), this, SLOT(NetNickTaken(const QString&)), Qt::QueuedConnection);
1398 connect(hwnet, SIGNAL(AuthFailed()), this, SLOT(NetAuthFailed()), Qt::QueuedConnection);
1399 //connect(ui.pageNetGame->BtnBack, SIGNAL(clicked()), hwnet, SLOT(partRoom()));
1400 connect(hwnet, SIGNAL(askForRoomPassword()), this, SLOT(askRoomPassword()), Qt::QueuedConnection);
1401
1402 ui.pageRoomsList->chatWidget->setUsersModel(hwnet->lobbyPlayersModel());
1403 ui.pageNetGame->chatWidget->setUsersModel(hwnet->roomPlayersModel());
1404
1405 // rooms list page stuff
1406 ui.pageRoomsList->setModel(hwnet->roomsListModel());
1407 connect(hwnet, SIGNAL(adminAccess(bool)),
1408 ui.pageRoomsList, SLOT(setAdmin(bool)), Qt::QueuedConnection);
1409 connect(hwnet, SIGNAL(adminAccess(bool)),
1410 ui.pageRoomsList->chatWidget, SLOT(adminAccess(bool)), Qt::QueuedConnection);
1411
1412 connect(hwnet, SIGNAL(serverMessage(const QString&)),
1413 ui.pageRoomsList->chatWidget, SLOT(onServerMessage(const QString&)), Qt::QueuedConnection);
1414
1415 connect(ui.pageRoomsList, SIGNAL(askForCreateRoom(const QString &, const QString &)),
1416 hwnet, SLOT(CreateRoom(const QString&, const QString &)));
1417 connect(ui.pageRoomsList, SIGNAL(askForJoinRoom(const QString &, const QString &)),
1418 hwnet, SLOT(JoinRoom(const QString&, const QString &)));
1419 // connect(ui.pageRoomsList, SIGNAL(askForCreateRoom(const QString &)),
1420 // this, SLOT(NetGameMaster()));
1421 // connect(ui.pageRoomsList, SIGNAL(askForJoinRoom(const QString &)),
1422 // this, SLOT(NetGameSlave()));
1423 connect(ui.pageRoomsList, SIGNAL(askForRoomList()),
1424 hwnet, SLOT(askRoomsList()));
1425
1426 // room status stuff
1427 connect(hwnet, SIGNAL(roomMaster(bool)),
1428 this, SLOT(NetGameChangeStatus(bool)));
1429
1430 // net page stuff
1431 connect(hwnet, SIGNAL(roomNameUpdated(const QString &)),
1432 ui.pageNetGame, SLOT(setRoomName(const QString &)), Qt::QueuedConnection);
1433 connect(hwnet, SIGNAL(roomChatAction(const QString&, const QString&)),
1434 ui.pageNetGame->chatWidget, SLOT(onChatAction(const QString&, const QString&)), Qt::QueuedConnection);
1435 connect(hwnet, SIGNAL(roomChatMessage(const QString&, const QString&)),
1436 ui.pageNetGame->chatWidget, SLOT(onChatMessage(const QString&, const QString&)), Qt::QueuedConnection);
1437
1438 connect(hwnet, SIGNAL(roomMaster(bool)),
1439 ui.pageNetGame->chatWidget, SLOT(adminAccess(bool)), Qt::QueuedConnection);
1440 connect(ui.pageNetGame->chatWidget, SIGNAL(chatLine(const QString&)),
1441 hwnet, SLOT(chatLineToNetWithEcho(const QString&)));
1442 connect(ui.pageNetGame->BtnGo, SIGNAL(clicked()), hwnet, SLOT(ToggleReady()));
1443 connect(hwnet, SIGNAL(setMyReadyStatus(bool)),
1444 ui.pageNetGame, SLOT(setReadyStatus(bool)), Qt::QueuedConnection);
1445
1446 // chat widget actions
1447 connect(ui.pageNetGame->chatWidget, SIGNAL(kick(const QString&)),
1448 hwnet, SLOT(kickPlayer(const QString&)));
1449 connect(ui.pageNetGame->chatWidget, SIGNAL(delegate(const QString&)),
1450 hwnet, SLOT(delegateToPlayer(const QString&)));
1451 connect(ui.pageNetGame->chatWidget, SIGNAL(ban(const QString&)),
1452 hwnet, SLOT(banPlayer(const QString&)));
1453 connect(ui.pageNetGame->chatWidget, SIGNAL(info(const QString&)),
1454 hwnet, SLOT(infoPlayer(const QString&)));
1455 connect(ui.pageNetGame->chatWidget, SIGNAL(follow(const QString&)),
1456 hwnet, SLOT(followPlayer(const QString&)));
1457 connect(ui.pageNetGame->chatWidget, SIGNAL(consoleCommand(const QString&)),
1458 hwnet, SLOT(consoleCommand(const QString&)));
1459 connect(ui.pageRoomsList->chatWidget, SIGNAL(kick(const QString&)),
1460 hwnet, SLOT(kickPlayer(const QString&)));
1461 connect(ui.pageRoomsList->chatWidget, SIGNAL(ban(const QString&)),
1462 hwnet, SLOT(banPlayer(const QString&)));
1463 connect(ui.pageRoomsList->chatWidget, SIGNAL(info(const QString&)),
1464 hwnet, SLOT(infoPlayer(const QString&)));
1465 connect(ui.pageRoomsList->chatWidget, SIGNAL(follow(const QString&)),
1466 hwnet, SLOT(followPlayer(const QString&)));
1467 connect(ui.pageRoomsList->chatWidget, SIGNAL(consoleCommand(const QString&)),
1468 hwnet, SLOT(consoleCommand(const QString&)));
1469
1470 // player info
1471 connect(hwnet, SIGNAL(playerInfo(const QString&, const QString&, const QString&, const QString&)),
1472 ui.pageRoomsList->chatWidget, SLOT(onPlayerInfo(const QString&, const QString&, const QString&, const QString&)), Qt::QueuedConnection);
1473 connect(hwnet, SIGNAL(playerInfo(const QString&, const QString&, const QString&, const QString&)),
1474 ui.pageNetGame->chatWidget, SLOT(onPlayerInfo(const QString&, const QString&, const QString&, const QString&)), Qt::QueuedConnection);
1475
1476 // chatting
1477 connect(ui.pageRoomsList->chatWidget, SIGNAL(chatLine(const QString&)),
1478 hwnet, SLOT(chatLineToLobby(const QString&)));
1479 connect(hwnet, SIGNAL(lobbyChatAction(const QString&,const QString&)),
1480 ui.pageRoomsList->chatWidget, SLOT(onChatAction(const QString&,const QString&)), Qt::QueuedConnection);
1481 connect(hwnet, SIGNAL(lobbyChatMessage(const QString&, const QString&)),
1482 ui.pageRoomsList->chatWidget, SLOT(onChatMessage(const QString&, const QString&)), Qt::QueuedConnection);
1483
1484 // nick list stuff
1485 {
1486 QSortFilterProxyModel * playersSortFilterModel = qobject_cast<QSortFilterProxyModel *>(hwnet->lobbyPlayersModel());
1487 if(playersSortFilterModel)
1488 {
1489 PlayersListModel * players = qobject_cast<PlayersListModel *>(playersSortFilterModel->sourceModel());
1490 connect(players, SIGNAL(nickAdded(const QString&, bool)),
1491 ui.pageNetGame->chatWidget, SLOT(nickAdded(const QString&, bool)));
1492 connect(players, SIGNAL(nickRemoved(const QString&)),
1493 ui.pageNetGame->chatWidget, SLOT(nickRemoved(const QString&)));
1494 connect(players, SIGNAL(nickAddedLobby(const QString&, bool)),
1495 ui.pageRoomsList->chatWidget, SLOT(nickAdded(const QString&, bool)));
1496 connect(players, SIGNAL(nickRemovedLobby(const QString&)),
1497 ui.pageRoomsList->chatWidget, SLOT(nickRemoved(const QString&)));
1498 connect(players, SIGNAL(nickRemovedLobby(const QString&, const QString&)),
1499 ui.pageRoomsList->chatWidget, SLOT(nickRemoved(const QString&, const QString&)));
1500 }
1501 }
1502
1503 // teams selecting stuff
1504 connect(ui.pageNetGame->pNetTeamsWidget, SIGNAL(hhogsNumChanged(const HWTeam&)),
1505 hwnet, SLOT(onHedgehogsNumChanged(const HWTeam&)));
1506 connect(ui.pageNetGame->pNetTeamsWidget, SIGNAL(teamColorChanged(const HWTeam&)),
1507 hwnet, SLOT(onTeamColorChanged(const HWTeam&)));
1508 connect(ui.pageNetGame->pNetTeamsWidget, SIGNAL(teamWillPlay(HWTeam)), hwnet, SLOT(AddTeam(HWTeam)));
1509 connect(ui.pageNetGame->pNetTeamsWidget, SIGNAL(acceptRequested(HWTeam)), hwnet, SLOT(AddTeam(HWTeam)));
1510 connect(ui.pageNetGame->pNetTeamsWidget, SIGNAL(teamNotPlaying(const HWTeam&)), hwnet, SLOT(RemoveTeam(const HWTeam&)));
1511 connect(hwnet, SIGNAL(hhnumChanged(const HWTeam&)),
1512 ui.pageNetGame->pNetTeamsWidget, SLOT(changeHHNum(const HWTeam&)), Qt::QueuedConnection);
1513 connect(hwnet, SIGNAL(teamColorChanged(const HWTeam&)),
1514 ui.pageNetGame->pNetTeamsWidget, SLOT(changeTeamColor(const HWTeam&)), Qt::QueuedConnection);
1515
1516 // admin stuff
1517 connect(hwnet, SIGNAL(serverMessageNew(const QString&)), ui.pageAdmin, SLOT(serverMessageNew(const QString &)));
1518 connect(hwnet, SIGNAL(serverMessageOld(const QString&)), ui.pageAdmin, SLOT(serverMessageOld(const QString &)));
1519 connect(hwnet, SIGNAL(latestProtocolVar(int)), ui.pageAdmin, SLOT(protocol(int)));
1520 connect(hwnet, SIGNAL(bansList(const QStringList &)), ui.pageAdmin, SLOT(setBansList(const QStringList &)));
1521 connect(ui.pageAdmin, SIGNAL(setServerMessageNew(const QString&)), hwnet, SLOT(setServerMessageNew(const QString &)));
1522 connect(ui.pageAdmin, SIGNAL(setServerMessageOld(const QString&)), hwnet, SLOT(setServerMessageOld(const QString &)));
1523 connect(ui.pageAdmin, SIGNAL(setProtocol(int)), hwnet, SLOT(setLatestProtocolVar(int)));
1524 connect(ui.pageAdmin, SIGNAL(askServerVars()), hwnet, SLOT(askServerVars()));
1525 connect(ui.pageAdmin, SIGNAL(clearAccountsCache()), hwnet, SLOT(clearAccountsCache()));
1526 connect(ui.pageAdmin, SIGNAL(bansListRequest()), hwnet, SLOT(getBanList()));
1527 connect(ui.pageAdmin, SIGNAL(removeBan(QString)), hwnet, SLOT(removeBan(QString)));
1528 connect(ui.pageAdmin, SIGNAL(banIP(QString,QString,int)), hwnet, SLOT(banIP(QString,QString,int)));
1529 connect(ui.pageAdmin, SIGNAL(banNick(QString,QString,int)), hwnet, SLOT(banNick(QString,QString,int)));
1530
1531 // disconnect
1532 connect(hwnet, SIGNAL(disconnected(const QString&)), this, SLOT(ForcedDisconnect(const QString&)), Qt::QueuedConnection);
1533
1534 // config stuff
1535 connect(hwnet, SIGNAL(paramChanged(const QString &, const QStringList &)), ui.pageNetGame->pGameCFG, SLOT(setParam(const QString &, const QStringList &)));
1536 connect(ui.pageNetGame->pGameCFG, SIGNAL(paramChanged(const QString &, const QStringList &)), hwnet, SLOT(onParamChanged(const QString &, const QStringList &)));
1537 connect(hwnet, SIGNAL(configAsked()), ui.pageNetGame->pGameCFG, SLOT(fullNetConfig()));
1538
1539 // using proxy slot to prevent loss of game messages when they're sent to not yet connected slot of game object
1540 connect(hwnet, SIGNAL(FromNet(const QByteArray &)), this, SLOT(FromNetProxySlot(const QByteArray &)), Qt::QueuedConnection);
1541
1542 //nick and pass stuff
1543 hwnet->m_private_game = !(hostName == NETGAME_DEFAULT_SERVER && port == NETGAME_DEFAULT_PORT);
1544 if (hwnet->m_private_game == false && AskForNickAndPwd() != 0)
1545 return;
1546
1547 QString nickname = config->value("net/nick", config->getRandomNick()).toString();
1548 ui.pageRoomsList->setUser(nickname);
1549 ui.pageNetGame->setUser(nickname);
1550
1551 hwnet->Connect(hostName, port, useTls, nickname);
1552 }
1553
AskForNickAndPwd(void)1554 int HWForm::AskForNickAndPwd(void)
1555 {
1556 //remove temppasswordhash just in case
1557 config->clearTempHash();
1558
1559 //initialize
1560 QString hash;
1561 QString temphash;
1562 QString nickname;
1563 QString password;
1564
1565 do {
1566 nickname = config->value("net/nick", config->getRandomNick()).toString();
1567 hash = config->passwordHash();
1568 temphash = config->tempHash();
1569
1570 //if something from login is missing, start dialog loop
1571 if (nickname.isEmpty() || hash.isEmpty()) {
1572 //open dialog
1573 HWPasswordDialog * pwDialog = new HWPasswordDialog(this);
1574 // make the "new account" button dialog open a browser with the registration page
1575 connect(pwDialog->pbNewAccount, SIGNAL(clicked()), this, SLOT(openRegistrationPage()));
1576 pwDialog->cbSave->setChecked(config->value("net/savepassword", true).toBool());
1577
1578 //if nickname is present, put it into the field
1579 if (!nickname.isEmpty()) {
1580 pwDialog->leNickname->setText(nickname);
1581 pwDialog->lePassword->setFocus();
1582 }
1583
1584 //if dialog aborted, return failure
1585 if (pwDialog->exec() != QDialog::Accepted) {
1586 delete pwDialog;
1587 GoBack();
1588 return 1;
1589 }
1590
1591 //set nick and pass from the dialog
1592 nickname = pwDialog->leNickname->text();
1593 password = pwDialog->lePassword->text();
1594 bool save = pwDialog->cbSave->isChecked();
1595 //clean up
1596 delete pwDialog;
1597
1598 //check the nickname variable
1599 if (nickname.isEmpty()) {
1600 int retry = RetryDialog(tr("Hedgewars - Empty nickname"), tr("No nickname supplied."));
1601 GoBack();
1602 if (retry) {
1603 if (hwnet->m_private_game) {
1604 QStringList list = hwnet->getHost().split(":");
1605 NetConnectServer(list.at(0), list.at(1).toShort(), false);
1606 } else
1607 NetConnectOfficialServer();
1608 }
1609 break; //loop restart
1610 } else {
1611 //update nickname if it's fine
1612 config->setValue("net/nick", nickname);
1613 config->updNetNick();
1614 }
1615
1616 //check the password variable
1617 if (password.isEmpty()) {
1618 config->clearPasswordHash();
1619 break;
1620 } else {
1621 //calculate temphash and set it into config
1622 temphash = QCryptographicHash::hash(password.toUtf8(), QCryptographicHash::Md5).toHex();
1623 config->setTempHash(temphash);
1624
1625 //if user wants to save password
1626 config->setValue("net/savepassword", save);
1627 if (save) {
1628 // user wants to save password
1629 ui.pageOptions->CBSavePassword->setChecked(true);
1630 config->setPasswordHash(temphash);
1631 }
1632 }
1633 }
1634 } while (nickname.isEmpty() || (hash.isEmpty() && temphash.isEmpty())); //while a nickname, or both hashes are missing
1635
1636 return 0;
1637 }
1638
NetConnect()1639 void HWForm::NetConnect()
1640 {
1641 HWHostPortDialog * hpd = new HWHostPortDialog(this);
1642 hpd->leHost->setText(*netHost);
1643 hpd->sbPort->setValue(netPort);
1644
1645 if (hpd->exec() == QDialog::Accepted)
1646 {
1647 config->SaveOptions();
1648 delete netHost;
1649 netHost = new QString(hpd->leHost->text());
1650 netPort = hpd->sbPort->value();
1651 NetConnectServer(*netHost, netPort, false);
1652 }
1653 delete hpd;
1654 }
1655
NetStartServer()1656 void HWForm::NetStartServer()
1657 {
1658 config->SaveOptions();
1659
1660 pnetserver = new HWNetServer;
1661 if (!pnetserver->StartServer(ui.pageNetServer->sbPort->value()))
1662 {
1663 MessageDialog::ShowErrorMessage(QMessageBox::tr("Unable to start server"), this);
1664
1665 delete pnetserver;
1666 pnetserver = 0;
1667 return;
1668 }
1669
1670 QTimer::singleShot(250, this, SLOT(AsyncNetServerStart()));
1671
1672 pRegisterServer = new HWNetUdpServer(0,
1673 ui.pageNetServer->leServerDescr->text(),
1674 ui.pageNetServer->sbPort->value());
1675 }
1676
AsyncNetServerStart()1677 void HWForm::AsyncNetServerStart()
1678 {
1679 NetConnectServer("localhost", pnetserver->getRunningPort(), false);
1680 }
1681
NetDisconnect()1682 void HWForm::NetDisconnect()
1683 {
1684 if(pnetserver)
1685 {
1686 if (pRegisterServer)
1687 {
1688 pRegisterServer->unregister();
1689 pRegisterServer = 0;
1690 }
1691
1692 pnetserver->StopServer();
1693 delete pnetserver;
1694 pnetserver = 0;
1695 }
1696
1697 if(hwnet)
1698 hwnet->Disconnect();
1699 }
1700
ForcedDisconnect(const QString & reason)1701 void HWForm::ForcedDisconnect(const QString & reason)
1702 {
1703 if (reason == "Reconnected too fast") { //TODO: this is a hack, which should be remade
1704 bool retry = RetryDialog(tr("Hedgewars - Connection error"), tr("You reconnected too fast.\nPlease wait a few seconds and try again."));
1705 if (retry) {
1706 if (hwnet->m_private_game) {
1707 QStringList list = hwnet->getHost().split(":");
1708 NetConnectServer(list.at(0), list.at(1).toUInt(), false);
1709 } else
1710 NetConnectOfficialServer();
1711 }
1712 else {
1713 while (ui.Pages->currentIndex() != ID_PAGE_NET
1714 && ui.Pages->currentIndex() != ID_PAGE_MAIN)
1715 {
1716 GoBack();
1717 }
1718 }
1719 return;
1720 }
1721 if (pnetserver)
1722 return; // we have server - let it care of all things
1723 if (hwnet && (reason != "bye"))
1724 {
1725 QString errorStr = QMessageBox::tr("The connection to the server is lost.") + (reason.isEmpty()?"":("\n\n" + HWNewNet::tr("Reason:") + "\n" + reason));
1726 MessageDialog::ShowErrorMessage(errorStr, this);
1727 }
1728
1729 while (ui.Pages->currentIndex() != ID_PAGE_NET
1730 && ui.Pages->currentIndex() != ID_PAGE_MAIN)
1731 {
1732 GoBack();
1733 }
1734 }
1735
NetRedirected(quint16 port)1736 void HWForm::NetRedirected(quint16 port)
1737 {
1738 QMessageBox questionMsg(this);
1739 questionMsg.setIcon(QMessageBox::Question);
1740 questionMsg.setWindowTitle(QMessageBox::tr("Server redirection"));
1741 questionMsg.setText(QMessageBox::tr("This server supports secure connections on port %1.\nWould you like to reconnect securely?").arg(port));
1742 questionMsg.setTextFormat(Qt::PlainText);
1743 questionMsg.setWindowModality(Qt::WindowModal);
1744 questionMsg.addButton(QMessageBox::Yes);
1745 questionMsg.addButton(QMessageBox::No);
1746
1747 if (questionMsg.exec() == QMessageBox::Yes)
1748 {
1749 QString host = hwnet->getHost().split(":").at(0);
1750 NetConnectServer(host, port, true);
1751 }
1752 else if (hwnet)
1753 {
1754 hwnet->ContinueConnection();
1755 }
1756 }
1757
NetConnected()1758 void HWForm::NetConnected()
1759 {
1760 GoToPage(ID_PAGE_ROOMSLIST);
1761 }
1762
NetGameEnter()1763 void HWForm::NetGameEnter()
1764 {
1765 ui.pageNetGame->chatWidget->clear();
1766 GoToPage(ID_PAGE_NETGAME);
1767 }
1768
AddNetTeam(const HWTeam & team)1769 void HWForm::AddNetTeam(const HWTeam& team)
1770 {
1771 ui.pageNetGame->pNetTeamsWidget->addTeam(team);
1772 }
1773
RemoveNetTeam(const HWTeam & team)1774 void HWForm::RemoveNetTeam(const HWTeam& team)
1775 {
1776 ui.pageNetGame->pNetTeamsWidget->removeNetTeam(team);
1777 }
1778
StartMPGame()1779 void HWForm::StartMPGame()
1780 {
1781 QString ammo;
1782 ammo = ui.pageMultiplayer->gameCFG->WeaponsName->itemData(
1783 ui.pageMultiplayer->gameCFG->WeaponsName->currentIndex()
1784 ).toString();
1785
1786 CreateGame(ui.pageMultiplayer->gameCFG, ui.pageMultiplayer->teamsSelect, ammo);
1787
1788 game->StartLocal();
1789 }
1790
GameStateChanged(GameState gameState)1791 void HWForm::GameStateChanged(GameState gameState)
1792 {
1793 quint8 id = ui.Pages->currentIndex();
1794 switch(gameState)
1795 {
1796 case gsStarted:
1797 {
1798 demoIsPresent = true;
1799 Music(false);
1800 if (wBackground) wBackground->stopAnimation();
1801 if (!hwnet || (!hwnet->isRoomChief() || !hwnet->isInRoom())) GoToPage(ID_PAGE_INGAME);
1802 ui.pageGameStats->clear();
1803 if (pRegisterServer)
1804 {
1805 pRegisterServer->unregister();
1806 pRegisterServer = 0;
1807 }
1808 //setVisible(false);
1809 setFocusPolicy(Qt::NoFocus);
1810 break;
1811 }
1812 case gsFinished:
1813 {
1814 //setVisible(true);
1815 setFocusPolicy(Qt::StrongFocus);
1816 if (id == ID_PAGE_INGAME) GoBack();
1817 Music(ui.pageOptions->CBFrontendMusic->isChecked());
1818 if (wBackground) wBackground->startAnimation();
1819 GoToPage(ID_PAGE_GAMESTATS);
1820 if (hwnet)
1821 {
1822 if (!game || !game->netSuspend)
1823 hwnet->gameFinished(true);
1824 // After a game, the local player might have pseudo-teams left
1825 // when rejoining a previously left game. This makes sure the
1826 // teams list is in a consistent state.
1827 ui.pageNetGame->cleanupFakeNetTeams();
1828 }
1829 if (game) game->netSuspend = false;
1830 break;
1831 }
1832 default:
1833 {
1834 //setVisible(true);
1835 setFocusPolicy(Qt::StrongFocus);
1836 quint8 id = ui.Pages->currentIndex();
1837 if (id == ID_PAGE_INGAME ||
1838 // was room chief and the game was aborted
1839 (hwnet && hwnet->isRoomChief() && hwnet->isInRoom() &&
1840 (gameState == gsInterrupted || gameState == gsStopped || gameState == gsDestroyed || gameState == gsHalted)))
1841 {
1842 if (id == ID_PAGE_INGAME) GoBack();
1843 Music(ui.pageOptions->CBFrontendMusic->isChecked());
1844 if (wBackground) wBackground->startAnimation();
1845 if (hwnet) hwnet->gameFinished(false);
1846 }
1847 if (gameState == gsHalted) close();
1848 };
1849 }
1850 }
1851
DemoPresenceChanged(bool hasDemo)1852 void HWForm::DemoPresenceChanged(bool hasDemo)
1853 {
1854 demoIsPresent = hasDemo;
1855 }
1856
CreateGame(GameCFGWidget * gamecfg,TeamSelWidget * pTeamSelWidget,QString ammo)1857 void HWForm::CreateGame(GameCFGWidget * gamecfg, TeamSelWidget* pTeamSelWidget, QString ammo)
1858 {
1859 game = new HWGame(config, gamecfg, ammo, pTeamSelWidget);
1860 connect(game, SIGNAL(CampStateChanged(int)), this, SLOT(UpdateCampaignPageProgress(int)));
1861 connect(game, SIGNAL(TrainingStateChanged(int)), this, SLOT(UpdateTrainingPageTeam(int)));
1862 connect(game, SIGNAL(GameStateChanged(GameState)), this, SLOT(GameStateChanged(GameState)));
1863 connect(game, SIGNAL(DemoPresenceChanged(bool)), this, SLOT(DemoPresenceChanged(bool)));
1864 connect(game, SIGNAL(GameStats(char, const QString &)), ui.pageGameStats, SLOT(GameStats(char, const QString &)));
1865 connect(game, SIGNAL(ErrorMessage(const QString &)), this, SLOT(ShowFatalErrorMessage(const QString &)), Qt::QueuedConnection);
1866 connect(game, SIGNAL(HaveRecord(RecordType, const QByteArray &)), this, SLOT(GetRecord(RecordType, const QByteArray &)));
1867 m_lastDemo = QByteArray();
1868 }
1869
GetRecord(RecordType type,const QByteArray & record)1870 void HWForm::GetRecord(RecordType type, const QByteArray & record)
1871 {
1872 if (type != rtNeither)
1873 {
1874 QString filename;
1875 QByteArray demo = record;
1876 QString recordFileName =
1877 config->appendDateTimeToRecordName() ?
1878 QDateTime::currentDateTime().toString("yyyy-MM-dd_hh-mm") :
1879 "LastRound";
1880
1881 recordFileName += "_" + *cRevisionString + "-" + *cHashString;
1882
1883 if (type == rtDemo)
1884 {
1885 demo.replace(QByteArray("\x02TL"), QByteArray("\x02TD"));
1886 demo.replace(QByteArray("\x02TN"), QByteArray("\x02TD"));
1887 demo.replace(QByteArray("\x02TS"), QByteArray("\x02TD"));
1888 filename = cfgdir->absolutePath() + "/Demos/" + recordFileName + "." + *cProtoVer + ".hwd";
1889 m_lastDemo = demo;
1890 }
1891 else
1892 {
1893 demo.replace(QByteArray("\x02TL"), QByteArray("\x02TS"));
1894 demo.replace(QByteArray("\x02TN"), QByteArray("\x02TS"));
1895 filename = cfgdir->absolutePath() + "/Saves/" + recordFileName + "." + *cProtoVer + ".hws";
1896 }
1897
1898 QFile demofile(filename);
1899 if (!demofile.open(QIODevice::WriteOnly))
1900 MessageDialog::ShowErrorMessage(tr("Cannot save record to file %1").arg(filename), this);
1901 else
1902 {
1903 demofile.write(demo);
1904 demofile.close();
1905 }
1906 }
1907
1908 ui.pageVideos->startEncoding(record);
1909 }
1910
startTraining(const QString & scriptName,const QString & subFolder)1911 void HWForm::startTraining(const QString & scriptName, const QString & subFolder)
1912 {
1913 CreateGame(0, 0, 0);
1914
1915 QString trainTeam = ui.pageTraining->CBTeam->currentText();
1916 game->StartTraining(scriptName, subFolder, trainTeam);
1917 }
1918
StartCampaign()1919 void HWForm::StartCampaign()
1920 {
1921 CreateGame(0, 0, 0);
1922 QString camp = ui.pageCampaign->CBCampaign->itemData(ui.pageCampaign->CBCampaign->currentIndex()).toString();
1923 QString miss = campaignMissionInfo[ui.pageCampaign->CBMission->currentIndex()].script;
1924 QString campTeam = ui.pageCampaign->CBTeam->currentText();
1925 game->StartCampaign(camp, miss, campTeam);
1926 }
1927
CreateNetGame()1928 void HWForm::CreateNetGame()
1929 {
1930 // go back in pages to prevent user from being stuck on certain pages
1931 if(ui.Pages->currentIndex() == ID_PAGE_GAMESTATS ||
1932 ui.Pages->currentIndex() == ID_PAGE_INGAME)
1933 GoBack();
1934
1935 QString ammo;
1936 ammo = ui.pageNetGame->pGameCFG->WeaponsName->itemData(
1937 ui.pageNetGame->pGameCFG->WeaponsName->currentIndex()
1938 ).toString();
1939
1940 CreateGame(ui.pageNetGame->pGameCFG, ui.pageNetGame->pNetTeamsWidget, ammo);
1941
1942 connect(game, SIGNAL(SendNet(const QByteArray &)), hwnet, SLOT(SendNet(const QByteArray &)));
1943 connect(game, SIGNAL(SendChat(const QString &)), hwnet, SLOT(chatLineToNet(const QString &)));
1944 connect(game, SIGNAL(SendConsoleCommand(const QString&)), hwnet, SLOT(consoleCommand(const QString&)));
1945 connect(game, SIGNAL(SendTeamMessage(const QString &)), hwnet, SLOT(SendTeamMessage(const QString &)));
1946 connect(hwnet, SIGNAL(chatStringFromNet(const QString &)), game, SLOT(FromNetChat(const QString &)), Qt::QueuedConnection);
1947 connect(hwnet, SIGNAL(Warning(const QString&)), game, SLOT(FromNetWarning(const QString&)), Qt::QueuedConnection);
1948 connect(hwnet, SIGNAL(Error(const QString&)), game, SLOT(FromNetError(const QString&)), Qt::QueuedConnection);
1949
1950 game->StartNet();
1951 }
1952
PlayOfficialServerDemo()1953 void HWForm::PlayOfficialServerDemo()
1954 {
1955 // go back in pages to prevent user from being stuck on certain pages
1956 if(ui.Pages->currentIndex() == ID_PAGE_GAMESTATS ||
1957 ui.Pages->currentIndex() == ID_PAGE_INGAME)
1958 GoBack();
1959
1960 QString ammo;
1961 ammo = ui.pageNetGame->pGameCFG->WeaponsName->itemData(
1962 ui.pageNetGame->pGameCFG->WeaponsName->currentIndex()
1963 ).toString();
1964
1965 CreateGame(ui.pageNetGame->pGameCFG, ui.pageNetGame->pNetTeamsWidget, ammo);
1966
1967 game->PlayOfficialServerDemo();
1968 }
1969
closeEvent(QCloseEvent * event)1970 void HWForm::closeEvent(QCloseEvent *event)
1971 {
1972 config->SaveOptions();
1973 #ifdef VIDEOREC
1974 config->SaveVideosOptions();
1975 #endif
1976 event->accept();
1977 }
1978
Music(bool checked)1979 void HWForm::Music(bool checked)
1980 {
1981 if (checked)
1982 SDLInteraction::instance().startMusic();
1983 else
1984 SDLInteraction::instance().stopMusic();
1985 }
1986
NetGameChangeStatus(bool isMaster)1987 void HWForm::NetGameChangeStatus(bool isMaster)
1988 {
1989 if (isMaster)
1990 NetGameMaster();
1991 else
1992 NetGameSlave();
1993 }
1994
NetGameMaster()1995 void HWForm::NetGameMaster()
1996 {
1997 ui.pageNetGame->setMasterMode(true);
1998 ui.pageNetGame->restrictJoins->setChecked(false);
1999 ui.pageNetGame->restrictTeamAdds->setChecked(false);
2000 ui.pageNetGame->restrictUnregistered->setChecked(false);
2001 ui.pageNetGame->pGameCFG->GameSchemes->setModel(gameSchemeModel);
2002 ui.pageNetGame->pGameCFG->setMaster(true);
2003 ui.pageNetGame->pNetTeamsWidget->setInteractivity(true);
2004 ui.pageNetGame->pGameCFG->resetSchemeStates();
2005
2006 if (hwnet)
2007 {
2008 // disconnect connections first to ensure their inexistance and not to connect twice
2009 ui.pageNetGame->BtnStart->disconnect(this);
2010 ui.pageNetGame->BtnUpdate->disconnect(hwnet);
2011 ui.pageNetGame->leRoomName->disconnect(hwnet);
2012 ui.pageNetGame->restrictJoins->disconnect(hwnet);
2013 ui.pageNetGame->restrictTeamAdds->disconnect(hwnet);
2014 ui.pageNetGame->restrictUnregistered->disconnect(hwnet);
2015 ui.pageNetGame->disconnect(hwnet, SLOT(updateRoomName(const QString&)));
2016
2017 ui.pageNetGame->setRoomName(hwnet->getRoom());
2018
2019 connect(ui.pageNetGame->BtnStart, SIGNAL(clicked()), this, SLOT(startGame()));
2020 connect(ui.pageNetGame, SIGNAL(askForUpdateRoomName(const QString &)), hwnet, SLOT(updateRoomName(const QString &)));
2021 connect(ui.pageNetGame->restrictJoins, SIGNAL(triggered()), hwnet, SLOT(toggleRestrictJoins()));
2022 connect(ui.pageNetGame->restrictTeamAdds, SIGNAL(triggered()), hwnet, SLOT(toggleRestrictTeamAdds()));
2023 connect(ui.pageNetGame->restrictUnregistered, SIGNAL(triggered()), hwnet, SLOT(toggleRegisteredOnly()));
2024 connect(ui.pageNetGame->pGameCFG->GameSchemes->model(),
2025 SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)),
2026 ui.pageNetGame->pGameCFG,
2027 SLOT(resendSchemeData())
2028 );
2029 }
2030 }
2031
NetGameSlave()2032 void HWForm::NetGameSlave()
2033 {
2034 ui.pageNetGame->pGameCFG->setMaster(false);
2035 ui.pageNetGame->pNetTeamsWidget->setInteractivity(false);
2036
2037 if (hwnet)
2038 {
2039 NetGameSchemeModel * netAmmo = new NetGameSchemeModel(hwnet);
2040 connect(hwnet, SIGNAL(netSchemeConfig(QStringList)), netAmmo, SLOT(setNetSchemeConfig(QStringList)));
2041
2042 ui.pageNetGame->pGameCFG->GameSchemes->setModel(netAmmo);
2043
2044 ui.pageNetGame->setRoomName(hwnet->getRoom());
2045
2046 ui.pageNetGame->pGameCFG->GameSchemes->view()->disconnect(hwnet);
2047 connect(hwnet, SIGNAL(netSchemeConfig(QStringList)),
2048 this, SLOT(selectFirstNetScheme()));
2049 }
2050
2051 ui.pageNetGame->setMasterMode(false);
2052 }
2053
FromNetProxySlot(const QByteArray & msg)2054 void HWForm::FromNetProxySlot(const QByteArray & msg)
2055 {
2056 if(game)
2057 game->FromNet(msg);
2058
2059 }
2060
selectFirstNetScheme()2061 void HWForm::selectFirstNetScheme()
2062 {
2063 ui.pageNetGame->pGameCFG->GameSchemes->setCurrentIndex(0);
2064 }
2065
NetLeftRoom(const QString & reason)2066 void HWForm::NetLeftRoom(const QString & reason)
2067 {
2068 if (ui.Pages->currentIndex() == ID_PAGE_NETGAME || ui.Pages->currentIndex() == ID_PAGE_INGAME)
2069 {
2070 GoBack();
2071 if (!reason.isEmpty())
2072 ui.pageRoomsList->displayNotice(reason);
2073 }
2074 else
2075 qWarning("Left room while not in room");
2076 }
2077
resizeEvent(QResizeEvent * event)2078 void HWForm::resizeEvent(QResizeEvent * event)
2079 {
2080 int w = event->size().width();
2081 int h = event->size().height();
2082 if (wBackground)
2083 {
2084 wBackground->setFixedSize(w, h);
2085 wBackground->move(0, 0);
2086 }
2087 }
2088
UpdateTrainingPageTeam(int index)2089 void HWForm::UpdateTrainingPageTeam(int index)
2090 {
2091 Q_UNUSED(index);
2092 HWTeam team(ui.pageTraining->CBTeam->currentText());
2093 QString tName = team.name();
2094
2095 QListWidget* listWidget;
2096 for(int w = 0; w < 3; w++)
2097 {
2098 switch(w) {
2099 case 0: listWidget = ui.pageTraining->lstTrainings; break;
2100 case 1: listWidget = ui.pageTraining->lstChallenges; break;
2101 case 2: listWidget = ui.pageTraining->lstScenarios; break;
2102 default: listWidget = ui.pageTraining->lstTrainings; break;
2103 }
2104 unsigned int n = listWidget->count();
2105
2106 for(unsigned int i = 0; i < n; i++)
2107 {
2108 QListWidgetItem* item = listWidget->item(i);
2109 QString missionName = QString(item->data(Qt::UserRole).toString()).replace(QString(" "),QString("_"));
2110 if(isMissionWon(missionName, tName))
2111 item->setIcon(finishedIcon);
2112 else
2113 item->setIcon(notFinishedIcon);
2114 }
2115 }
2116 ui.pageTraining->updateInfo();
2117 }
2118
InitCampaignPage()2119 void HWForm::InitCampaignPage()
2120 {
2121 ui.pageCampaign->CBCampaign->clear();
2122 HWTeam team(ui.pageCampaign->CBTeam->currentText());
2123
2124 QStringList entries = DataManager::instance().entryList(
2125 "Missions/Campaign",
2126 QDir::Dirs,
2127 QStringList("[^\\.]*")
2128 );
2129
2130 unsigned int n = entries.count();
2131
2132 for(unsigned int i = 0; i < n; i++)
2133 {
2134 const QString & campaignName = entries[i];
2135 QString tName = team.name();
2136 ui.pageCampaign->CBCampaign->addItem(getRealCampName(campaignName), campaignName);
2137 }
2138
2139 }
2140
RestoreSingleplayerTeamSelection()2141 void HWForm::RestoreSingleplayerTeamSelection()
2142 {
2143 QString lastTeam = config->value("frontend/lastSingleplayerTeam", QString()).toString();
2144 if (!lastTeam.isNull() && !lastTeam.isEmpty())
2145 {
2146 int index = ui.pageCampaign->CBTeam->findData(lastTeam, Qt::DisplayRole);
2147 if(index != -1)
2148 {
2149 ui.pageCampaign->CBTeam->setCurrentIndex(index);
2150 UpdateCampaignPageTeam(index);
2151 }
2152 index = ui.pageTraining->CBTeam->findData(lastTeam, Qt::DisplayRole);
2153 if(index != -1)
2154 {
2155 ui.pageTraining->CBTeam->setCurrentIndex(index);
2156 UpdateTrainingPageTeam(index);
2157 }
2158 }
2159 }
2160
UpdateCampaignPage(int index)2161 void HWForm::UpdateCampaignPage(int index)
2162 {
2163 Q_UNUSED(index);
2164 HWTeam team(ui.pageCampaign->CBTeam->currentText());
2165 QString campaignName = ui.pageCampaign->CBCampaign->currentData().toString();
2166 QString tName = team.name();
2167
2168 campaignMissionInfo = getCampMissionList(campaignName,tName);
2169 ui.pageCampaign->CBMission->clear();
2170
2171 // Populate mission list
2172 for(int i=0;i<campaignMissionInfo.size();i++)
2173 {
2174 ui.pageCampaign->CBMission->addItem(QString(campaignMissionInfo[i].realName), QString(campaignMissionInfo[i].name));
2175 if(isCampMissionWon(campaignName, i, tName))
2176 ui.pageCampaign->CBMission->setItemIcon(i, finishedIcon);
2177 else
2178 ui.pageCampaign->CBMission->setItemIcon(i, notFinishedIcon);
2179 }
2180
2181 // Select first open mission
2182 int missionIndex = ui.pageCampaign->CBMission->currentIndex();
2183 if(isCampMissionWon(campaignName, missionIndex, tName))
2184 {
2185 for(int m = 0; m < ui.pageCampaign->CBMission->count(); m++)
2186 {
2187 if(!isCampMissionWon(campaignName, m, tName))
2188 {
2189 ui.pageCampaign->CBMission->setCurrentIndex(m);
2190 break;
2191 }
2192 }
2193 }
2194 }
2195
UpdateCampaignPageTeam(int index)2196 void HWForm::UpdateCampaignPageTeam(int index)
2197 {
2198 Q_UNUSED(index);
2199 HWTeam team(ui.pageCampaign->CBTeam->currentText());
2200 QString tName = team.name();
2201
2202 QStringList entries = DataManager::instance().entryList(
2203 "Missions/Campaign",
2204 QDir::Dirs,
2205 QStringList("[^\\.]*")
2206 );
2207
2208 unsigned int n = entries.count();
2209
2210 // Update campaign status
2211 for(unsigned int i = 0; i < n; i++)
2212 {
2213 QString campaignName = QString(entries[i]).replace(QString(" "),QString("_"));
2214 if(isCampWon(campaignName, tName))
2215 ui.pageCampaign->CBCampaign->setItemIcon(i, finishedIcon);
2216 else
2217 ui.pageCampaign->CBCampaign->setItemIcon(i, notFinishedIcon);
2218 }
2219 }
2220
UpdateCampaignPageMission(int index)2221 void HWForm::UpdateCampaignPageMission(int index)
2222 {
2223 // update thumbnail and description
2224 QString campaignName = ui.pageCampaign->CBCampaign->currentData().toString();
2225 // when campaign changes the UpdateCampaignPageMission is triggered with wrong values
2226 // this will cause segfault. This check prevents illegal memory reads
2227 if(index > -1 && index < campaignMissionInfo.count()) {
2228 ui.pageCampaign->lbltitle->setText("<h2>"+ui.pageCampaign->CBMission->currentText()+"</h2>");
2229 ui.pageCampaign->lbldescription->setText(campaignMissionInfo[index].description);
2230 ui.pageCampaign->btnPreview->setIcon(QIcon(campaignMissionInfo[index].image));
2231 }
2232 }
2233
UpdateCampaignPageProgress(int index)2234 void HWForm::UpdateCampaignPageProgress(int index)
2235 {
2236 QString missionTitle = ui.pageCampaign->CBMission->currentData().toString();
2237 UpdateCampaignPage(0);
2238 int missionIndex = 0;
2239 // Restore selected mission (because UpdateCampaignPage repopulated the list)
2240 for(int i=0;i<ui.pageCampaign->CBMission->count();i++)
2241 {
2242 if (ui.pageCampaign->CBMission->itemData(i).toString() == missionTitle)
2243 {
2244 missionIndex = i;
2245 break;
2246 }
2247 }
2248
2249 // Get metadata
2250 int c = ui.pageCampaign->CBCampaign->currentIndex();
2251 QString campaignName = ui.pageCampaign->CBCampaign->itemData(c).toString();
2252 HWTeam team(ui.pageCampaign->CBTeam->currentText());
2253 QString tName = team.name();
2254
2255 if(index == gsFinished)
2256 {
2257 // Select new mission when current mission went from
2258 // unfinished to finished.
2259 if(ui.pageCampaign->currentMissionWon == false &&
2260 isCampMissionWon(campaignName, missionIndex, tName))
2261 {
2262 // Traverse all missions and pick first mission that
2263 // has not been won.
2264 bool selected = false;
2265 // start from mission that comes after the selected one
2266 for(int m = missionIndex-1; m >= 0;m--)
2267 {
2268 if(!isCampMissionWon(campaignName, m, tName))
2269 {
2270 missionIndex = m;
2271 selected = true;
2272 break;
2273 }
2274 }
2275 // No mission selected? Let's try again from the end of the list
2276 if(!selected)
2277 {
2278 for(int m = ui.pageCampaign->CBMission->count()-1; m > missionIndex-1; m--)
2279 {
2280 if(!isCampMissionWon(campaignName, m, tName))
2281 {
2282 missionIndex = m;
2283 break;
2284 }
2285 }
2286 }
2287 // If no mission was selected, the old selection remains unchanged.
2288 }
2289 }
2290 else if(index == gsStarted)
2291 {
2292 // Remember the "won" state of current mission before we start it.
2293 // We'll need it when the game has finished.
2294 ui.pageCampaign->currentMissionWon = isCampMissionWon(campaignName, missionIndex, tName);
2295 }
2296
2297 ui.pageCampaign->CBMission->setCurrentIndex(missionIndex);
2298
2299 // Update campaign victory status
2300 if(isCampWon(campaignName, tName))
2301 ui.pageCampaign->CBCampaign->setItemIcon(c, finishedIcon);
2302 else
2303 ui.pageCampaign->CBCampaign->setItemIcon(c, notFinishedIcon);
2304 }
2305
getDemoArguments()2306 QString HWForm::getDemoArguments()
2307 {
2308
2309 QString prefix = "\"" + datadir->absolutePath() + "\"";
2310 QString userPrefix = "\"" + cfgdir->absolutePath() + "\"";
2311 #ifdef Q_OS_WIN
2312 prefix = prefix.replace("/","\\");
2313 userPrefix = userPrefix.replace("/","\\");
2314 #endif
2315
2316 std::pair<QRect, QRect> resolutions = config->vid_ResolutionPair();
2317 return QString("--prefix " + prefix
2318 + " --user-prefix " + userPrefix
2319 + " --fullscreen-width " + QString::number(resolutions.first.width())
2320 + " --fullscreen-height " + QString::number(resolutions.first.height())
2321 + " --width " + QString::number(resolutions.second.width())
2322 + " --height " + QString::number(resolutions.second.height())
2323 + (config->vid_Maximized() ? " --maximized" : "")
2324 + (config->zoom() == 100 ? "" : " --zoom " + QString::number(config->zoom()))
2325 + " --volume " + QString::number(config->volume())
2326 + (config->isMusicEnabled() ? "" : " --nomusic")
2327 + (config->isSoundEnabled() ? "" : " --nosound")
2328 + (config->isAudioDampenEnabled() ? "" : " --nodampen")
2329 + " --locale " + config->language() + ".txt"
2330 + (config->vid_Fullscreen() ? " --fullscreen" : "")
2331 + (config->isShowFPSEnabled() ? " --showfps" : "")
2332 + (config->isAltDamageEnabled() ? " --altdmg" : "")
2333 + " --frame-interval " + QString::number(config->timerInterval())
2334 + " --raw-quality " + QString::number(config->translateQuality()))
2335 + (!config->Form->ui.pageOptions->CBTeamTag->isChecked() ? " --no-teamtag" : "")
2336 + (!config->Form->ui.pageOptions->CBHogTag->isChecked() ? " --no-hogtag" : "")
2337 + (!config->Form->ui.pageOptions->CBHealthTag->isChecked() ? " --no-healthtag" : "")
2338 + (config->Form->ui.pageOptions->CBTagOpacity->isChecked() ? " --translucent-tags" : "")
2339 + (!config->isHolidaySillinessEnabled() ? " --no-holiday-silliness" : "");
2340 }
2341
AssociateFiles()2342 void HWForm::AssociateFiles()
2343 {
2344 bool success = true;
2345 QString arguments = getDemoArguments();
2346 #ifdef _WIN32
2347 QSettings registry_hkcr("HKEY_CLASSES_ROOT", QSettings::NativeFormat);
2348
2349 // file extension(s)
2350 registry_hkcr.setValue(".hwd/Default", "Hedgewars.Demo");
2351 registry_hkcr.setValue(".hws/Default", "Hedgewars.Save");
2352 registry_hkcr.setValue("Hedgewars.Demo/Default", tr("Hedgewars Demo File", "File Types"));
2353 registry_hkcr.setValue("Hedgewars.Save/Default", tr("Hedgewars Save File", "File Types"));
2354 registry_hkcr.setValue("Hedgewars.Demo/DefaultIcon/Default", "\"" + bindir->absolutePath().replace("/", "\\") + "\\hwdfile.ico\",0");
2355 registry_hkcr.setValue("Hedgewars.Save/DefaultIcon/Default", "\"" + bindir->absolutePath().replace("/", "\\") + "\\hwsfile.ico\",0");
2356 registry_hkcr.setValue("Hedgewars.Demo/Shell/Open/Command/Default", "\"" + bindir->absolutePath().replace("/", "\\") + "\\hwengine.exe\" " + arguments + " %1");
2357 registry_hkcr.setValue("Hedgewars.Save/Shell/Open/Command/Default", "\"" + bindir->absolutePath().replace("/", "\\") + "\\hwengine.exe\" " + arguments + " %1");
2358
2359 // custom url scheme(s)
2360 registry_hkcr.setValue("hwplay/Default", "\"URL:Hedgewars ServerAccess Scheme\"");
2361 registry_hkcr.setValue("hwplay/URL Protocol", "");
2362 registry_hkcr.setValue("hwplay/DefaultIcon/Default", "\"" + bindir->absolutePath().replace("/", "\\") + "\\hedgewars.exe\",0");
2363 registry_hkcr.setValue("hwplay/Shell/Open/Command/Default", "\"" + bindir->absolutePath().replace("/", "\\") + "\\hedgewars.exe\" %1");
2364 #elif defined __APPLE__
2365 // only useful when other apps have taken precedence over our file extensions and you want to reset it
2366 system("defaults write com.apple.LaunchServices LSHandlers -array-add '<dict><key>LSHandlerContentTag</key><string>hwd</string><key>LSHandlerContentTagClass</key><string>public.filename-extension</string><key>LSHandlerRoleAll</key><string>org.hedgewars.desktop</string></dict>'");
2367 system("defaults write com.apple.LaunchServices LSHandlers -array-add '<dict><key>LSHandlerContentTag</key><string>hws</string><key>LSHandlerContentTagClass</key><string>public.filename-extension</string><key>LSHandlerRoleAll</key><string>org.hedgewars.desktop</string></dict>'");
2368 system("/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/Support/lsregister -kill -domain local -domain system -domain user");
2369 #else
2370 // this is a little silly due to all the system commands below anyway - just use mkdir -p ? Does have the advantage of the alert I guess
2371 if (success) success = checkForDir(QDir::home().absolutePath() + "/.local");
2372 if (success) success = checkForDir(QDir::home().absolutePath() + "/.local/share");
2373 if (success) success = checkForDir(QDir::home().absolutePath() + "/.local/share/mime");
2374 if (success) success = checkForDir(QDir::home().absolutePath() + "/.local/share/mime/packages");
2375 if (success) success = checkForDir(QDir::home().absolutePath() + "/.local");
2376 if (success) success = checkForDir(QDir::home().absolutePath() + "/.local/share");
2377 if (success) success = checkForDir(QDir::home().absolutePath() + "/.local/share/applications");
2378 if (success) success = system(("cp "+datadir->absolutePath()+"/misc/hedgewars-mimeinfo.xml "+QDir::home().absolutePath()+"/.local/share/mime/packages").toLocal8Bit().constData())==0;
2379 if (success) success = system(("cp "+datadir->absolutePath()+"/misc/hedgewars.desktop "+QDir::home().absolutePath()+"/.local/share/applications").toLocal8Bit().constData())==0;
2380 if (success) success = system(("cp "+datadir->absolutePath()+"/misc/hwengine.desktop "+QDir::home().absolutePath()+"/.local/share/applications").toLocal8Bit().constData())==0;
2381 if (success) success = system(("update-mime-database "+QDir::home().absolutePath()+"/.local/share/mime").toLocal8Bit().constData())==0;
2382 if (success) success = system("xdg-mime default hedgewars.desktop x-scheme-handler/hwplay")==0;
2383 if (success) success = system("xdg-mime default hwengine.desktop application/x-hedgewars-demo")==0;
2384 if (success) success = system("xdg-mime default hwengine.desktop application/x-hedgewars-save")==0;
2385 // hack to add user's settings to hwengine. might be better at this point to read in the file, append it, and write it out to its new home. This assumes no spaces in the data dir path
2386 if (success) success = system(("sed -i 's|^\\(Exec=.*\\) \\(%f\\)|\\1 \\2 "+arguments+"|' "+QDir::home().absolutePath()+"/.local/share/applications/hwengine.desktop").toLocal8Bit().constData())==0;
2387 #endif
2388 if (success)
2389 {
2390 QMessageBox infoMsg(this);
2391 infoMsg.setIcon(QMessageBox::Information);
2392 infoMsg.setWindowTitle(QMessageBox::tr("Hedgewars - Success"));
2393 infoMsg.setText(QMessageBox::tr("All file associations have been set"));
2394 infoMsg.setTextFormat(Qt::PlainText);
2395 infoMsg.setWindowModality(Qt::WindowModal);
2396 infoMsg.exec();
2397 }
2398 else
2399 MessageDialog::ShowErrorMessage(QMessageBox::tr("File association failed."), this);
2400 }
2401
openRegistrationPage()2402 void HWForm::openRegistrationPage()
2403 {
2404 QDesktopServices::openUrl(QUrl("https://www.hedgewars.org/user/register"));
2405 }
2406
saveDemoWithCustomName()2407 void HWForm::saveDemoWithCustomName()
2408 {
2409 if(!m_lastDemo.isEmpty())
2410 {
2411 QString fileName;
2412 bool ok = false;
2413 do
2414 {
2415 fileName = QInputDialog::getText(this, tr("Demo name"), tr("Demo name:"));
2416
2417 if(!fileName.isEmpty())
2418 {
2419 QString filePath = cfgdir->absolutePath() + "/Demos/" + fileName + "." + *cProtoVer + ".hwd";
2420 QFile demofile(filePath);
2421 ok = demofile.open(QIODevice::WriteOnly);
2422 if (!ok)
2423 MessageDialog::ShowErrorMessage(tr("Cannot save record to file %1").arg(filePath), this);
2424 else
2425 {
2426 ok = -1 != demofile.write(m_lastDemo);
2427 demofile.close();
2428 }
2429 }
2430 }
2431 while(!fileName.isEmpty() && !ok);
2432 }
2433 }
2434
restartGame()2435 void HWForm::restartGame()
2436 {
2437 // get rid off old game stats page
2438 if(ui.Pages->currentIndex() == ID_PAGE_GAMESTATS)
2439 GoBack();
2440
2441 CreateGame(lastGameCfg, lastGameTeamSel, lastGameAmmo);
2442
2443 switch(lastGameType) {
2444 case gtTraining:
2445 game->StartTraining(lastGameStartArgs.at(0).toString(), lastGameStartArgs.at(1).toString(), lastGameStartArgs.at(2).toString());
2446 break;
2447 case gtQLocal:
2448 game->StartQuick();
2449 break;
2450 case gtCampaign:
2451 game->StartCampaign(lastGameStartArgs.at(0).toString(), lastGameStartArgs.at(1).toString(), lastGameStartArgs.at(2).toString());
2452 break;
2453 case gtLocal:
2454 game->StartLocal();
2455 break;
2456 case gtDemo:
2457 case gtSave:
2458 PlayDemo();
2459 break;
2460 default:
2461 break;
2462 }
2463 }
2464
ShowFatalErrorMessage(const QString & msg)2465 void HWForm::ShowFatalErrorMessage(const QString & msg)
2466 {
2467 MessageDialog::ShowFatalMessage(msg, this);
2468 }
2469
showFeedbackDialog()2470 void HWForm::showFeedbackDialog()
2471 {
2472 QNetworkRequest newRequest(QUrl("https://www.hedgewars.org"));
2473
2474 QNetworkAccessManager *manager = new QNetworkAccessManager(this);
2475 QNetworkReply *reply = manager->get(newRequest);
2476 connect(reply, SIGNAL(finished()), this, SLOT(showFeedbackDialogNetChecked()));
2477 }
2478
showFeedbackDialogNetChecked()2479 void HWForm::showFeedbackDialogNetChecked()
2480 {
2481 QNetworkReply *reply = qobject_cast<QNetworkReply *>(sender());
2482
2483 if (reply) {
2484 switch (reply->error()) {
2485 case QNetworkReply::NoError:
2486 {
2487 FeedbackDialog dialog(this);
2488 dialog.exec();
2489 }
2490 break;
2491 case QNetworkReply::UnknownNetworkError:
2492 MessageDialog::ShowFatalMessage(
2493 tr("Unknown network error (possibly missing SSL library)."), this);
2494 break;
2495 default:
2496 MessageDialog::ShowFatalMessage(
2497 QString(tr("This feature requires an Internet connection, but you don't appear to be online (error code: %1).")).arg(reply->error()), this);
2498 break;
2499 }
2500 }
2501 else {
2502 MessageDialog::ShowFatalMessage(tr("Internal error: Reply object is invalid."), this);
2503 }
2504 }
2505
startGame()2506 void HWForm::startGame()
2507 {
2508 QMessageBox questionMsg(this);
2509 questionMsg.setIcon(QMessageBox::Question);
2510 questionMsg.setWindowTitle(QMessageBox::tr("Not all players are ready"));
2511 questionMsg.setText(QMessageBox::tr("Are you sure you want to start this game?\nNot all players are ready."));
2512 questionMsg.setTextFormat(Qt::PlainText);
2513 questionMsg.setWindowModality(Qt::WindowModal);
2514 questionMsg.addButton(QMessageBox::Yes);
2515 questionMsg.addButton(QMessageBox::Cancel);
2516
2517 if (hwnet->allPlayersReady() || questionMsg.exec() == QMessageBox::Yes)
2518 hwnet->startGame();
2519 }
2520