Implemented most of the saved game file parser.
[GoMoku3D.git] / src / gui / MainWindow.cpp
blob8ac62384fa2b1e77d376407fa3de59390d058592
1 /********************************************************************
3 * Copyright (C) 2008 Davide Pesavento and Daniele Battaglia
5 * This file is part of GoMoku3D.
7 * GoMoku3D is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
12 * GoMoku3D is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with GoMoku3D. If not, see <http://www.gnu.org/licenses/>.
20 *******************************************************************/
22 #include <QMessageBox>
23 #include <QFileDialog>
25 #include "MainWindow.h"
26 #include "GameLoop.h"
27 #include "GameMatrix.h"
28 #include "AIPlayer.h"
29 #include "RemotePlayer.h"
30 #include "HumanPlayer.h"
31 #include "RenderWidget.h"
32 #include "HistoryModel.h"
33 #include "ChatWidget.h"
34 #include "TimerWidget.h"
35 #include "Network.h"
36 #include "OnlineDialog.h"
37 #include "Suggester.h"
38 #include "SettingsManager.h"
39 #include "GUISettings.h"
40 #include "Preferences.h"
41 #include "PlayersWidget.h"
42 #include "StandAloneDialog.h"
43 #include "ServerSettingsDialog.h"
45 MainWindow::MainWindow() : QMainWindow(), Ui_MainWindow(*this)
47 _loop = 0;
48 _net = 0;
49 _settings = 0;
50 _suggester = 0;
51 _history = 0;
52 _actualPlayerIndex = -1;
53 _isStandAloneGame = false;
54 _isOnlineGame = false;
55 _isServerGame = false;
56 _isDedicatedServer = false;
57 _timerDuration = 0;
59 setupUi(this);
61 _render = new RenderWidget(this);
62 setCentralWidget(_render);
64 QDockWidget *playersDock = new QDockWidget(this);
65 QDockWidget *historyDock = new QDockWidget(this);
66 QDockWidget *timerDock = new QDockWidget(this);
67 QDockWidget *chatDock = new QDockWidget(this);
68 playersDock->setWindowTitle(tr("Players' Info"));
69 historyDock->setWindowTitle(tr("History"));
70 timerDock->setWindowTitle(tr("Timer"));
71 chatDock->setWindowTitle(tr("Chat"));
72 playersDock->setFeatures(QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable);
73 historyDock->setFeatures(QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable);
74 timerDock->setFeatures(QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable);
75 chatDock->setFeatures(QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable);
76 historyDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
77 timerDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
78 chatDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
79 _playersWidget = new PlayersWidget(playersDock);
80 _historyView = new QTableView(historyDock);
81 _timer = new TimerWidget(timerDock);
82 _chat = new ChatWidget(chatDock);
83 playersDock->setWidget(_playersWidget);
84 historyDock->setWidget(_historyView);
85 timerDock->setWidget(_timer);
86 chatDock->setWidget(_chat);
87 addDockWidget(Qt::LeftDockWidgetArea, playersDock);
88 addDockWidget(Qt::LeftDockWidgetArea, historyDock);
89 addDockWidget(Qt::RightDockWidgetArea, timerDock);
90 addDockWidget(Qt::RightDockWidgetArea, chatDock);
93 _history = new HistoryModel(this, &_playersInfo);
94 _historyView->setModel(_history);
97 connect(_timer, SIGNAL(timerExpired()), this, SLOT(timerExpired()));
98 connect(_historyView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(undoMoves(QModelIndex)));
99 connect(_historyView, SIGNAL(clicked(QModelIndex)), this, SLOT(showPastGameStatus(QModelIndex)));
100 connect(actionShow_last_move, SIGNAL(triggered()), this, SLOT(showLastMove()));
101 connect(actionSuggest_move, SIGNAL(triggered()), this, SLOT(suggestMove()));
102 connect(actionTake_back_move, SIGNAL(triggered()), this, SLOT(undoTurn()));
103 connect(actionHost_Game, SIGNAL(triggered()), this, SLOT(newServerGame()));
104 connect(actionConnect_To_Server, SIGNAL(triggered()), this, SLOT(newOnlineGame()));
105 connect(actionNew_local_game,SIGNAL(triggered()), this, SLOT(newStandAloneGame()));
106 connect(actionAbout_Qt, SIGNAL(triggered()), qApp, SLOT(aboutQt()));
107 connect(actionPreferences, SIGNAL(triggered()), this, SLOT(showPreferences()));
108 connect(actionQuit, SIGNAL(triggered()), this, SLOT(close()));
109 connect(actionSave_game, SIGNAL(triggered()), this, SLOT(saveGame()));
112 MainWindow::~MainWindow()
114 GUISettings set;
115 set.setGeometry(this->saveGeometry());
116 //set.setDockState(this->saveState());
118 if (_loop != 0) {
119 _loop->stop();
122 delete _render;
124 if (_suggester != 0) {
125 _suggester->wait();
126 _suggester->deleteLater();
129 if (_net != 0) {
130 delete _net;
133 if (_loop != 0) {
134 _loop->wait();
135 _loop->deleteLater();
138 //TODO c'e' altro da fare?
141 void MainWindow::drawMove(Move move)
143 if (_playersInfo.isEmpty()) {
144 return;
147 QColor color;
148 if (move.playerId() == 0) {
149 color = _playersInfo.at(0).color();
151 if (move.playerId() == 1) {
152 color = _playersInfo.at(1).color();
154 if (move.playerId() == 2) {
155 color = _playersInfo.at(2).color();
158 bool ok = move.isValid();
159 _timer->stop();
160 if (ok) {
161 _history->append(move);
163 else {
164 // messaggio di errore
165 // se alla drawMove arriva un point nullo cosa vuol dire?
166 // - dal network: scaduto il timer del giocatore
167 // - quando viene rilasciato il lock in fase di distruzione ma non dovrebbe succedere niente
168 if (!_isDedicatedServer) {
169 _render->acceptMove(false);
171 QMessageBox::information(this, tr("Game Ended"), tr("Timer expired:\n%1 lost the game.").arg(_playersInfo.at(move.playerId()).name()), QMessageBox::Ok);
172 disableOnEndGame();
175 if (!_isDedicatedServer && ok) {
176 if (!_actualViewIndex.isValid() || (_actualViewIndex.row() == _history->moveList().size() - 2)) {
177 _render->drawMove(move.point(), color);
178 _actualViewIndex = _history->index(_history->moveList().size() - 1, 0);
180 actionShow_last_move->setEnabled(true);
181 if (_isStandAloneGame) {
182 actionTake_back_move->setEnabled(true);
185 else {
186 actionShow_last_move->setEnabled(false);
187 actionTake_back_move->setEnabled(false);
191 QModelIndex MainWindow::actualViewIndex() const
193 return _actualViewIndex;
196 bool MainWindow::askUserConfirmation()
198 QMessageBox::StandardButton result = QMessageBox::question(this,
199 tr("Abandon Game?"),
200 tr("If you continue, the current\ngame will be lost.\n\nAre you sure you want to continue?"),
201 QMessageBox::Yes | QMessageBox::No);
203 if (result == QMessageBox::Yes) {
204 return true;
205 } else {
206 return false;
210 void MainWindow::newStandAloneGame()
212 if (_loop != 0 && !askUserConfirmation()) {
213 return;
216 resetOnNewGame();
218 qobject_cast<QDockWidget*>(_timer->parent())->setVisible(false);
219 qobject_cast<QDockWidget*>(_chat->parent())->setVisible(false);
220 qobject_cast<QDockWidget*>(_playersWidget->parent())->setVisible(true);
222 StandAloneDialog dialog(this);
223 dialog.exec();
224 if (dialog.result() == QDialog::Rejected) {
225 return;
228 _isStandAloneGame = true;
230 // abilito alcune voci del menu
231 actionLoad_game->setEnabled(true);
232 actionSave_game->setEnabled(true);
234 int d1 = dialog.d1Box->currentText().toInt();
235 int d2 = dialog.d2Box->currentText().toInt();
236 int numPlayers = dialog.numPlayersBox->currentText().toInt();
237 Q_ASSERT(d1 != 0);
238 Q_ASSERT(d2 != 0);
239 Q_ASSERT(numPlayers != 0);
240 GameMatrix::create(d1, d2, numPlayers);
241 Q_ASSERT(GameMatrix::instance() != 0);
243 _render->init(d1 * d2);
245 QString type;
246 QList<Player*> players;
247 if (dialog.human_0->isChecked()) {
248 type = "H";
249 players.append(new HumanPlayer(0, _render));
251 else {
252 type = "A";
253 players.append(new AIPlayer(0));
255 _playersInfo.append(PlayerInfo(dialog.playername_0->text(), dialog.color_0->palette().color(QPalette::Button), type));
257 if (dialog.human_1->isChecked()) {
258 type = "H";
259 players.append(new HumanPlayer(1, _render));
261 else {
262 type = "A";
263 players.append(new AIPlayer(1));
265 _playersInfo.append(PlayerInfo(dialog.playername_1->text(), dialog.color_1->palette().color(QPalette::Button), type));
267 if (numPlayers == 3) {
268 if (dialog.human_2->isChecked()) {
269 type = "H";
270 players.append(new HumanPlayer(2, _render));
272 else {
273 type = "A";
274 players.append(new AIPlayer(2));
276 _playersInfo.append(PlayerInfo(dialog.playername_2->text(), dialog.color_2->palette().color(QPalette::Button), type));
279 _playersWidget->set(&_playersInfo);
280 connectAISkillSlider(players);
282 qobject_cast<QDockWidget*>(_historyView->parent())->setVisible(true);
284 //costruisco GL
285 _loop = new GameLoop(players);
286 //move to thread dei player
287 Player *p;
288 foreach (p, players) {
289 p->moveToThread(_loop);
292 connect(actionForce_Move, SIGNAL(triggered()), _loop, SLOT(forceMove()));
293 connect(_loop, SIGNAL(turn(int)), this, SLOT(turn(int)));
294 connect(_loop, SIGNAL(turn(int)), _playersWidget, SLOT(updateTurn(int)));
295 connect(_loop, SIGNAL(moved(Move)), this, SLOT(drawMove(Move)));
296 connect(_loop, SIGNAL(win(int)), this, SLOT(playerWin(int)));
297 connect(_loop, SIGNAL(draw(QList<int>)), this, SLOT(playersDraw(QList<int>)));
299 _loop->start();
302 void MainWindow::newOnlineGame()
304 if (_loop != 0 && !askUserConfirmation()) {
305 return;
308 resetOnNewGame();
310 qobject_cast<QDockWidget*>(_timer->parent())->setVisible(true);
311 qobject_cast<QDockWidget*>(_chat->parent())->setVisible(true);
312 qobject_cast<QDockWidget*>(_playersWidget->parent())->setVisible(true);
314 OnlineDialog dialog(this);
315 dialog.exec();
316 if (dialog.result() == QDialog::Rejected) {
317 if (_net != 0) {
318 delete _net;
319 _net = 0;
321 return;
323 connect(_net, SIGNAL(error(QString)), this, SLOT(networkError(QString)));
325 _isOnlineGame = true;
327 //TODO connette i segnali di errore del net con MW
329 //estraggo i dati
330 int d1 = dialog.d1Label->text().toInt();
331 int d2 = dialog.d2Label->text().toInt();
332 int numPlayers = dialog.numPlayersLabel->text().toInt();
333 GameMatrix::create(d1, d2, numPlayers);
335 _render->init(d1 * d2);
337 actionShow_last_move->setEnabled(true);
338 //estraggo e costruisco la lista di playersInfo
339 //costruisco i player
340 QList<Player*> players;
341 if (dialog._typeList[0] == "H") {
342 players.append(new HumanPlayer(0, _render));
344 else {
345 players.append(new RemotePlayer(0, _net));
347 _playersInfo.append(PlayerInfo(dialog.name_0->text(), dialog.color_0->palette().color(QPalette::Button), dialog._typeList[0]));
348 if (dialog._typeList[1] == "H") {
349 players.append(new HumanPlayer(1, _render));
351 else {
352 players.append(new RemotePlayer(1, _net));
354 _playersInfo.append(PlayerInfo(dialog.name_1->text(), dialog.color_1->palette().color(QPalette::Button), dialog._typeList[1]));
355 if (numPlayers == 3) {
356 if (dialog._typeList[2] == "H") {
357 players.append(new HumanPlayer(2, _render));
359 else {
360 players.append(new RemotePlayer(2, _net));
362 _playersInfo.append(PlayerInfo(dialog.name_2->text(), dialog.color_2->palette().color(QPalette::Button), dialog._typeList[2]));
366 qobject_cast<QDockWidget*>(_historyView->parent())->setVisible(true);
368 _playersWidget->set(&_playersInfo);
370 _net->setupChat(_chat);
372 int t = dialog.timerLabel->text().toInt();
373 _timerDuration = t;
374 _timer->setDuration(t);
376 //costruisco GL
377 _loop = new GameLoop(players);
379 QList<Move> history = _history->moveList();
380 if (history.size() != 0) {
381 Move m;
382 GameMatrix *mat = GameMatrix::instance();
383 QList<bool> win;
384 for (int i = 0; i < _playersInfo.size(); i++) {
385 win.append(false);
387 foreach (m, history) {
388 win[m.playerId()] = mat->add(m);
389 _render->drawMove(m.point(), _playersInfo.at(m.playerId()).color());
391 _loop->winStatus(win);
392 _loop->onLoadSetTurn(history.size() % _playersInfo.size());
394 //move to thread dei player
395 Player *p;
396 foreach (p, players) {
397 p->moveToThread(_loop);
400 _net->setupGameLoop(_loop);
402 //connetto
403 connect(_loop, SIGNAL(turn(int)), this, SLOT(turn(int)));
404 connect(_loop, SIGNAL(turn(int)), _playersWidget, SLOT(updateTurn(int)));
405 connect(_loop, SIGNAL(moved(Move)), this, SLOT(drawMove(Move)));
406 connect(_loop, SIGNAL(win(int)), this, SLOT(playerWin(int)));
407 connect(_loop, SIGNAL(draw(QList<int>)), this, SLOT(playersDraw(QList<int>)));
409 _loop->start();
412 void MainWindow::updateHistory(QList<Move> list)
414 Move m;
415 foreach (m, list) {
416 _history->append(m);
418 _actualViewIndex = _history->index(_history->moveList().size() - 1, 0);
421 void MainWindow::newServerGame()
423 if (_loop != 0 && !askUserConfirmation()) {
424 return;
427 resetOnNewGame();
428 qobject_cast<QDockWidget*>(_timer->parent())->setVisible(false);
429 qobject_cast<QDockWidget*>(_chat->parent())->setVisible(false);
430 qobject_cast<QDockWidget*>(_playersWidget->parent())->setVisible(false);
431 qobject_cast<QDockWidget*>(_historyView->parent())->setVisible(false);
433 ServerSettingsDialog dialog(this);
434 dialog.exec();
435 if (dialog.result() == QDialog::Rejected) {
436 if (_net != 0) {
437 delete _net;
438 _net = 0;
440 return;
443 connect(_net, SIGNAL(error(QString)), this, SLOT(networkError(QString)));
445 int d1 = dialog.d1Box->currentText().toInt();
446 int d2 = dialog.d2Box->currentText().toInt();
447 int numPlayers = dialog.numPlayersBox->currentText().toInt();
449 GameMatrix::create(d1, d2, numPlayers);
451 QString type;
452 QList<Player*> players;
453 if (dialog.human_0->isChecked()) {
454 type = "H";
455 actionShow_last_move->setEnabled(true);
456 players.append(new HumanPlayer(0, _render));
458 else {
459 if (dialog.ai_0->isChecked()) {
460 type = "A";
461 players.append(new AIPlayer(0));
463 else {
464 type = "R";
465 players.append(new RemotePlayer(0, _net));
468 _playersInfo.append(PlayerInfo(dialog.playername_0->text(), dialog.color_0->palette().color(QPalette::Button), type));
469 if (dialog.human_1->isChecked()) {
470 type = "H";
471 actionShow_last_move->setEnabled(true);
472 players.append(new HumanPlayer(1, _render));
474 else {
475 if (dialog.ai_1->isChecked()) {
476 type = "A";
477 players.append(new AIPlayer(1));
479 else {
480 type = "R";
481 players.append(new RemotePlayer(1, _net));
484 _playersInfo.append(PlayerInfo(dialog.playername_1->text(), dialog.color_1->palette().color(QPalette::Button), type));
485 if (numPlayers == 3) {
486 if (dialog.human_2->isChecked()) {
487 type = "H";
488 actionShow_last_move->setEnabled(true);
489 players.append(new HumanPlayer(2, _render));
491 else {
492 if (dialog.ai_2->isChecked()) {
493 type = "A";
494 players.append(new AIPlayer(2));
496 else {
497 type = "R";
498 players.append(new RemotePlayer(2, _net));
501 _playersInfo.append(PlayerInfo(dialog.playername_2->text(), dialog.color_2->palette().color(QPalette::Button), type));
504 int t = dialog.timerSpinBox->value();
505 _timerDuration = t;
506 _timer->setDuration(t);
508 //costruisco GL
509 _loop = new GameLoop(players);
510 //move to thread dei player
511 Player *p;
512 foreach (p, players) {
513 p->moveToThread(_loop);
515 connectAISkillSlider(players);
517 _net->setupGameLoop(_loop);
519 bool noHuman = true;
520 for (int i = 0; i < _playersInfo.size(); i++) {
521 if (_playersInfo.at(i).type() == "H") {
522 noHuman = false;
526 //TODO connette i segnali di errore del net con MW
528 if (noHuman && dialog.dedicatedserver->isChecked()) {
529 // modalità server dedicato
530 _isDedicatedServer = true;
531 qobject_cast<QDockWidget*>(_playersWidget->parent())->setVisible(true);
532 // TODO: bisogna connettere qualcosa?
533 } else {
534 // modalità giocatore o spettatore
535 _isServerGame = true;
536 //TODO
537 _render->init(d1 * d2);
538 _net->setupChat(_chat);
539 qobject_cast<QDockWidget*>(_timer->parent())->setVisible(true);
540 qobject_cast<QDockWidget*>(_chat->parent())->setVisible(true);
541 qobject_cast<QDockWidget*>(_playersWidget->parent())->setVisible(true);
542 qobject_cast<QDockWidget*>(_historyView->parent())->setVisible(true);
544 _playersWidget->set(&_playersInfo);
545 _timer->setDuration(dialog.timerSpinBox->value());
547 //connetto
548 connect(_loop, SIGNAL(turn(int)), this, SLOT(turn(int)));
549 connect(_loop, SIGNAL(moved(Move)), this, SLOT(drawMove(Move)));
550 connect(_loop, SIGNAL(win(int)), this, SLOT(playerWin(int)));
551 connect(_loop, SIGNAL(draw(QList<int>)), this, SLOT(playersDraw(QList<int>)));
554 connect(_loop, SIGNAL(turn(int)), _playersWidget, SLOT(updateTurn(int))); //FIXME: perché qui?
556 _loop->start();
559 void MainWindow::showPreferences()
561 Preferences pref(this);
562 int result = pref.exec();
563 if (result == QDialog::Accepted) {
564 _render->updateBackground();
568 void MainWindow::disableOnEndGame()
570 //disabilita menu
571 actionTake_back_move->setEnabled(false);
572 actionSuggest_move->setEnabled(false);
573 actionForce_Move->setEnabled(false);
574 actionSave_game->setEnabled(false);
575 actionShow_last_move->setEnabled(false);
576 _playersWidget->arrow_0->setEnabled(false);
577 _playersWidget->arrow_1->setEnabled(false);
578 _playersWidget->arrow_2->setEnabled(false);
581 void MainWindow::playerWin(int playerId)
583 disableOnEndGame();
584 QMessageBox::information(this, tr("Game Ended"), tr("And the winner is ...\n%1!").arg(_playersInfo.at(playerId).name()), QMessageBox::Ok);
587 void MainWindow::playersDraw(QList<int> playersIds)
589 disableOnEndGame();
590 QString result;
591 int id;
592 foreach (id, playersIds) {
593 result += "\n - " + _playersInfo.at(id).name();
595 QMessageBox::information(this, tr("Game Ended"), tr("Game ended in a draw:%1").arg(result), QMessageBox::Ok);
598 void MainWindow::turn(int playerId)
600 _actualPlayerIndex = playerId;
601 if (_playersInfo.at(playerId).type() == "H" && _isStandAloneGame) {
602 actionSuggest_move->setEnabled(true);
604 else {
605 actionSuggest_move->setEnabled(false);
607 if (_playersInfo.at(playerId).type() == "A") {
608 actionForce_Move->setEnabled(true);
610 else {
611 actionForce_Move->setEnabled(false);
613 //TODO ?
614 bool startTimer = _playersInfo.at(playerId).type() == "H";
615 if (!_isStandAloneGame && _timerDuration > 0 && startTimer) {
616 _timer->start();
620 void MainWindow::resetOnNewGame()
622 //disabilito alcune voci del menù
623 disableOnEndGame();
625 if (_loop != 0) {
626 _loop->stop();
629 _render->reset();
631 if (_suggester != 0) {
632 _suggester->wait();
633 _suggester->deleteLater();
634 _suggester = 0;
637 if (_net != 0) {
638 delete _net;
639 _net = 0;
642 if (_loop != 0) {
643 _loop->wait();
644 _loop->deleteLater();
645 _loop = 0;
648 if (_chat != 0) {
649 _chat->reset();
652 if (_playersWidget != 0) {
653 _playersWidget->reset();
656 if (_settings != 0) {
657 delete _settings;
658 _settings = 0;
661 _playersInfo.clear();
663 if (_history != 0) {
664 _history->reset();
667 _isStandAloneGame = false;
668 _isOnlineGame = false;
669 _isServerGame = false;
670 _isDedicatedServer = false;
671 _actualViewIndex = QModelIndex();
672 _actualPlayerIndex = 0;
673 _timerDuration = 0;
677 void MainWindow::suggestMove()
679 if (_isStandAloneGame) {
680 if (_suggester != 0) {
681 _suggester->wait();
682 _suggester->deleteLater();
683 _suggester = 0;
685 _suggester = new Suggester(_actualPlayerIndex);
686 connect(_suggester, SIGNAL(suggestedMoveReady(Point)), _render, SLOT(highlightPoint(Point)));
687 _suggester->start();
691 bool MainWindow::isOnlineGame() const
693 return _isOnlineGame;
696 void MainWindow::connectAISkillSlider(const QList<Player*> &players)
698 AIPlayer *ai0 = qobject_cast<AIPlayer*>(players.at(0));
699 if (ai0 != 0) {
700 connect(_playersWidget->skill_0, SIGNAL(valueChanged(int)), ai0, SLOT(setSkill(int)), Qt::DirectConnection);
702 AIPlayer *ai1 = qobject_cast<AIPlayer*>(players.at(1));
703 if (ai1 != 0) {
704 connect(_playersWidget->skill_1, SIGNAL(valueChanged(int)), ai1, SLOT(setSkill(int)), Qt::DirectConnection);
706 if (players.size() == 3) {
707 AIPlayer *ai2 = qobject_cast<AIPlayer*>(players.at(2));
708 if (ai2 != 0) {
709 connect(_playersWidget->skill_2, SIGNAL(valueChanged(int)), ai2, SLOT(setSkill(int)), Qt::DirectConnection);
714 void MainWindow::showLastMove()
716 if (_history && !_isDedicatedServer && !_history->moveList().isEmpty()) {
717 _render->highlightPoint(_history->moveList().last().point());
721 void MainWindow::showPastGameStatus(QModelIndex index)
723 if (index == _actualViewIndex) {
724 return;
726 QList<Point> pointList;
727 QList<QColor> colorList;
728 if (_actualViewIndex < index) {
729 QList<Move> list = _history->moveList();
730 for (int i = _actualViewIndex.row() + 1; i <= index.row(); i++) {
731 pointList.append(list.at(i).point());
732 int id = list.at(i).playerId();
733 colorList.append(_playersInfo.at(id).color());
736 else {
737 QList<Move> list = _history->moveList();
738 for (int i = index.row() + 1; i <= _actualViewIndex.row(); i++) {
739 pointList.append(list.at(i).point());
742 _render->uncheckedDraw(pointList, colorList);
743 _actualViewIndex = index;
744 if (index.row() == _history->moveList().size() - 1 && _playersInfo.at(_actualPlayerIndex).type() == "H") {
745 _render->acceptMove(true);
747 else {
748 _render->acceptMove(false);
752 void MainWindow::undoTurn()
754 int ply = _history->moveList().size();
755 if (ply <= _playersInfo.size()) {
756 return;
758 QModelIndex end = _history->index(ply - _playersInfo.size() - 1, 0);
759 undoMoves(end);
762 void MainWindow::undoMoves(QModelIndex index)
764 if (!_isStandAloneGame) {
765 return;
767 if (_playersInfo.at(_actualPlayerIndex).type() != "H") {
768 QMessageBox::information(this, tr("Forbidden"), tr("You can't go back when it's not your turn."), QMessageBox::Ok);
769 return;
771 if (_loop->isRunning()) {
772 QMessageBox::StandardButton result = QMessageBox::question(this, tr("Are you sure?"), tr("Do you really want to bring the game in a past situation?"), QMessageBox::Yes | QMessageBox::No);
773 if (result == QMessageBox::No) {
774 return;
777 QList<Move> list = _history->moveList();
778 if (index.row() == list.size() - 1) {
779 return;
782 Q_ASSERT(_isStandAloneGame);
784 showPastGameStatus(index);
786 GameMatrix *mat = GameMatrix::instance();
787 QList<Point> lastRound;
788 for (int i = index.row(); i > index.row() - _playersInfo.size() && i >= 0; i--) {
789 lastRound.append(list.at(i).point());
791 while (lastRound.size() < _playersInfo.size()) {
792 lastRound.append(Point());
794 mat->setLastRound(lastRound);
795 for (int i = index.row() + 1; i < list.size(); i++) {
796 mat->clear(list.at(i).point());
798 int ply = list.size() - index.row() - 1;
799 _history->remove(ply);
800 _loop->setTurn(ply);
801 SyncSharedCondition::instance()->notifyMove(Point());
805 void MainWindow::timerExpired()
807 SyncSharedCondition *sync = SyncSharedCondition::instance();
808 QMutexLocker locker(sync);
809 sync->notifyMove(Point());
812 void MainWindow::networkError(QString error)
814 QMessageBox::critical(this, tr("Error"), tr("%1\nQuitting current game.").arg(error), QMessageBox::Ok);
815 resetOnNewGame();
818 void MainWindow::saveGame()
820 //FIXME aggiungere altri controlli?
821 Q_ASSERT(_isStandAloneGame);
822 QString fileName = QFileDialog::getSaveFileName(this, tr("Save Game"));
823 if (fileName.isEmpty()) {
824 return;
827 GUISettings set;
829 QFile f(fileName);
830 if (!f.open(QIODevice::WriteOnly)) {
831 //FIXME
832 return;
835 QXmlStreamWriter xml(&f);
836 xml.writeStartDocument();
837 xml.writeDefaultNamespace("http://www.itworks.org/GoMoku3D/SavedGame");
838 xml.writeStartElement("game");
839 xml.writeStartElement("settings");
840 xml.writeTextElement("difficultyOne", QString::number(GameMatrix::instance()->d1()));
841 xml.writeTextElement("difficultyTwo", QString::number(GameMatrix::instance()->d2()));
842 xml.writeStartElement("cubeColor");
843 xml.writeTextElement("r", QString::number(set.defaultCubeColor().red()));
844 xml.writeTextElement("g", QString::number(set.defaultCubeColor().green()));
845 xml.writeTextElement("b", QString::number(set.defaultCubeColor().blue()));
846 xml.writeTextElement("a", QString::number(set.defaultCubeColor().alpha()));
847 xml.writeEndElement();
848 xml.writeStartElement("backgroundColor");
849 xml.writeTextElement("r", QString::number(set.backgroundColor().red()));
850 xml.writeTextElement("g", QString::number(set.backgroundColor().green()));
851 xml.writeTextElement("b", QString::number(set.backgroundColor().blue()));
852 xml.writeTextElement("a", QString::number(set.backgroundColor().alpha()));
853 xml.writeEndElement();
854 xml.writeEndElement();
855 xml.writeStartElement("players");
856 for (int i = 0; i < _playersInfo.size(); i++) {
857 xml.writeStartElement("player");
858 xml.writeAttribute("id", QString::number(i));
859 xml.writeTextElement("name", _playersInfo.at(i).name());
860 xml.writeStartElement("color");
861 xml.writeTextElement("r", QString::number(_playersInfo.at(i).color().red()));
862 xml.writeTextElement("g", QString::number(_playersInfo.at(i).color().green()));
863 xml.writeTextElement("b", QString::number(_playersInfo.at(i).color().blue()));
864 xml.writeTextElement("a", QString::number(_playersInfo.at(i).color().alpha()));
865 xml.writeEndElement();
866 xml.writeTextElement("type", _playersInfo.at(i).type());
867 xml.writeEndElement();
869 xml.writeEndElement();
870 xml.writeStartElement("history");
871 Move m;
872 foreach (m, _history->moveList()) {
873 xml.writeStartElement("move");
874 xml.writeTextElement("player", QString::number(m.playerId()));
875 xml.writeStartElement("point");
876 xml.writeTextElement("x", QString::number(m.point().x()));
877 xml.writeTextElement("y", QString::number(m.point().y()));
878 xml.writeTextElement("z", QString::number(m.point().z()));
879 xml.writeEndElement();
880 xml.writeEndElement();
882 xml.writeEndElement();
883 xml.writeEndElement();
884 xml.writeEndDocument();
885 f.close();
888 void MainWindow::loadGame()
890 QString fileName = QFileDialog::getOpenFileName(this, tr("Open Saved Game"));
891 if (fileName.isEmpty()) {
892 return;
895 QFile f(fileName);
896 if (!f.open(QIODevice::ReadOnly)) {
897 //TODO: messaggio di errore
898 return;
901 QXmlStreamReader xml(&f);
902 bool success = xml.readNext() == QXmlStreamReader::StartDocument
903 && xml.readNext() == QXmlStreamReader::StartElement
904 && xml.name() == "game"
905 && parseSettings(xml)
906 && parsePlayers(xml)
907 && parseHistory(xml)
908 && xml.readNext() == QXmlStreamReader::EndElement
909 && xml.readNext() == QXmlStreamReader::EndDocument;
910 f.close();
911 if (!success) {
912 //TODO: messaggio di errore
913 qCritical() << "XML error while parsing file:" << xml.errorString();
914 return;
916 // TODO
919 bool MainWindow::parseSettings(QXmlStreamReader &xml)
921 if (xml.atEnd()) {
922 return false;
924 return xml.readNext() == QXmlStreamReader::StartElement
925 && xml.name() == "settings"
926 && parseInt(xml, "difficultyOne")
927 && parseInt(xml, "difficultyTwo")
928 && parseColor(xml, "cubeColor")
929 && parseColor(xml, "backgroundColor")
930 && xml.readNext() == QXmlStreamReader::EndElement;
933 bool MainWindow::parsePlayers(QXmlStreamReader &xml)
935 if (xml.atEnd()) {
936 return false;
938 //TODO
939 return true;
942 bool MainWindow::parseHistory(QXmlStreamReader &xml)
944 if (xml.atEnd()) {
945 return false;
947 if (xml.readNext() != QXmlStreamReader::StartElement || xml.name() != "history") {
948 return false;
950 int i = 0;
951 while (!xml.atEnd()) {
952 QXmlStreamReader::TokenType token = xml.readNext();
953 if (token == QXmlStreamReader::StartElement && xml.name() == "move") {
954 if (!parseMove(xml, i)) {
955 return false;
957 i++;
958 } else if (xml.readNext() == QXmlStreamReader::EndElement) {
959 break;
960 } else {
961 return false;
964 if (xml.atEnd()) {
965 return false;
967 setProperty("history/size", QVariant(i));
968 return true;
971 bool MainWindow::parseMove(QXmlStreamReader &xml, int n)
973 if (xml.atEnd()) {
974 return false;
976 bool success = parseInt(xml, "player")
977 && parsePoint(xml)
978 && xml.readNext() == QXmlStreamReader::EndElement;
979 if (success) {
980 Move m(property("player").toInt(), property("point").value<Point>());
981 setProperty(qPrintable("move_" + QString::number(n)), QVariant::fromValue(m));
983 return success;
986 bool MainWindow::parsePoint(QXmlStreamReader &xml)
988 if (xml.atEnd()) {
989 return false;
991 bool success = xml.readNext() == QXmlStreamReader::StartElement
992 && xml.name() == "point"
993 && parseInt(xml, "x")
994 && parseInt(xml, "y")
995 && parseInt(xml, "z")
996 && xml.readNext() == QXmlStreamReader::EndElement;
997 if (success) {
998 Point p(property("x").toInt(), property("y").toInt(), property("z").toInt());
999 setProperty("point", QVariant::fromValue(p));
1001 return success;
1004 bool MainWindow::parseInt(QXmlStreamReader &xml, QString elementName)
1006 if (xml.atEnd()) {
1007 return false;
1009 if (xml.readNext() != QXmlStreamReader::StartElement || xml.name() != elementName) {
1010 return false;
1012 bool ok;
1013 int x = xml.readElementText().toInt(&ok);
1014 if (!ok) {
1015 return false;
1017 setProperty(qPrintable(elementName), QVariant(x));
1018 return (xml.readNext() == QXmlStreamReader::EndElement);
1021 bool MainWindow::parseString(QXmlStreamReader &xml, QString elementName)
1023 if (xml.atEnd()) {
1024 return false;
1026 if (xml.readNext() != QXmlStreamReader::StartElement || xml.name() != elementName) {
1027 return false;
1029 setProperty(qPrintable(elementName), QVariant(xml.readElementText()));
1030 return (xml.readNext() == QXmlStreamReader::EndElement);
1033 bool MainWindow::parseColor(QXmlStreamReader &xml, QString elementName)
1035 if (xml.atEnd()) {
1036 return false;
1038 bool success = xml.readNext() == QXmlStreamReader::StartElement
1039 && xml.name() == elementName
1040 && parseInt(xml, "r")
1041 && parseInt(xml, "g")
1042 && parseInt(xml, "b")
1043 && parseInt(xml, "a")
1044 && xml.readNext() == QXmlStreamReader::EndElement;
1045 if (success) {
1046 QColor c(property("r").toInt(), property("g").toInt(), property("b").toInt(), property("a").toInt());
1047 setProperty(qPrintable(elementName), QVariant(c));
1049 return success;