2 Copyright 2004-2005 Andi Peredri <andi@ukr.net>
3 Copyright 2007 Simon Hürlimann <simon.huerlimann@huerlisi.ch>
4 Copyright 2007-2008 Fela Winkelmolen <fela.kde@gmail.com>
6 This program is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include "mainwindow.h"
23 #include <QPushButton>
27 #include <QPaintEvent>
28 #include <QGridLayout>
29 #include <QCloseEvent>
30 #include <QApplication>
33 #include <KIconLoader>
35 #include <KScoreDialog>
36 #include <KStandardAction>
38 #include <KActionCollection>
39 #include <KStandardGameAction>
41 #include <KNotification>
42 #include <KNotifyConfigWidget>
43 #include <KStandardDirs>
44 #include <KSelectAction>
45 #include <KGameDifficulty>
55 #include "abstractgrid.h"
57 static QMap
<Directions
, Directions
> contrdirs
;
59 MainWindow::MainWindow(QWidget
*parent
)
60 : KXmlGuiWindow(parent
)
62 kDebug() << Settings::skill();
66 contrdirs
[Right
] = Left
;
68 contrdirs
[Left
] = Right
;
72 statusBar()->insertPermanentItem("abcdefghijklmnopqrst: 0 ", 1, 1);
73 statusBar()->setItemAlignment(1, Qt::AlignLeft
& Qt::AlignVCenter
);
74 setAutoSaveSettings();
80 pixmapCache
= new QPixmap(centralWidget()->size());
81 m_invalidCache
= true;
83 // default values of KConfig XT don't seem to work
84 // this works around it. TODO: see why
87 KGameDifficulty::init(this, this, SLOT(startNewGame()));
88 KGameDifficulty::addStandardLevel(KGameDifficulty::Easy
);
89 KGameDifficulty::addStandardLevel(KGameDifficulty::Medium
);
90 KGameDifficulty::addStandardLevel(KGameDifficulty::Hard
);
91 KGameDifficulty::addStandardLevel(KGameDifficulty::VeryHard
);
93 if (Settings::skill() == 0) {
94 KGameDifficulty::setLevel(KGameDifficulty::Easy
);
96 KGameDifficulty::setLevel(
97 (KGameDifficulty::standardLevel
) (Settings::skill()) );
99 kDebug() << KGameDifficulty::levelString() << Settings::skill();
106 MainWindow::~MainWindow()
111 void MainWindow::createBoard()
113 View
* view
= new View(this);
114 view
->setFrameStyle(QFrame::NoFrame
);
115 view
->setMinimumSize(MinimumWidth
, MinimumHeight
);
117 gridLayout
= new QGridLayout(view
);
118 gridLayout
->setMargin(0);
119 gridLayout
->setSpacing(0);
120 setCentralWidget(view
);
123 for (int i
= 0; i
< MasterBoardSize
* MasterBoardSize
; i
++) {
124 board
[i
] = new Cell(view
, i
);
125 gridLayout
->addWidget(board
[i
], i
/ MasterBoardSize
, i
% MasterBoardSize
);
126 //board[i]->resize(32, 32); //TODO: needed ???
127 connect(board
[i
], SIGNAL(lClicked(int)), SLOT(lClicked(int)));
128 connect(board
[i
], SIGNAL(rClicked(int)), SLOT(rClicked(int)));
129 connect(board
[i
], SIGNAL(mClicked(int)), SLOT(mClicked(int)));
131 connect(board
[i
], SIGNAL(connectionsChanged()),
132 SLOT(updateConnections()));
134 board
[i
]->setWhatsThis(i18n("<h3>Rules of Game</h3><p>You are the "
135 "system administrator and your goal is to connect each terminal and "
136 "each cable to the central server.</p><p>Click the right mouse's "
137 "button for turning the cable in a clockwise direction, and left "
138 "mouse's button for turning the cable in a counter-clockwise "
139 "direction.</p><p>Start the LAN with as few turns as possible!</p>"));
143 void MainWindow::setBoardSize(int size
)
145 if (!(size
%2) || size
> MasterBoardSize
) {
146 kDebug() << "Wrong size!!\n";
149 //TODO: boardSize = size
150 int start
= (MasterBoardSize
- size
)/2;
151 for (int i
= 0; i
< start
; ++i
) {
152 // TODO: set only the last rows and columns to zero not half at the beginning and half at the end
153 gridLayout
->setRowStretch(i
, 0);
154 gridLayout
->setRowStretch(MasterBoardSize
- 1 - i
, 0);
155 gridLayout
->setColumnStretch(i
, 0);
156 gridLayout
->setColumnStretch(MasterBoardSize
- 1 - i
, 0);
159 for (int i
= start
; i
< start
+ size
; ++i
) {
160 gridLayout
->setRowStretch(i
, 1);
161 gridLayout
->setColumnStretch(i
, 1);
165 void MainWindow::setupActions()
168 KStandardGameAction::gameNew(this, SLOT(startNewGame()),
171 KStandardGameAction::highscores(this, SLOT(showHighscores()),
174 KStandardGameAction::quit(this, SLOT(close()), actionCollection());
177 KStandardAction::configureNotifications(this,
178 SLOT(configureNotifications()), actionCollection());
182 void MainWindow::showHighscores()
184 KScoreDialog
ksdialog(KScoreDialog::Name
, this);
188 void MainWindow::startNewGame()
192 KGameDifficulty::standardLevel l
= KGameDifficulty::level();
193 Settings::setSkill((int) l
);
195 if (l
== KGameDifficulty::VeryHard
) {
201 Settings::self()->writeConfig();
204 QString clicks
= i18nc("Number of mouse clicks", "Moves: %1", m_clickcount
);
205 statusBar()->changeItem(clicks
, 1);
207 KNotification::event( "startsound", i18n("New Game") );
209 for (int i
= 0; i
< MasterBoardSize
* MasterBoardSize
; i
++) {
210 board
[i
]->setDirs(None
);
211 board
[i
]->setConnected(false);
212 board
[i
]->setRoot(false);
213 board
[i
]->setLocked(false);
216 const int size
= boardSize();
219 // TODO: enable wrapped
221 grid
.initializeGrid(size
, size
, (Wrapping
)wrapped
);
223 const int start
= (MasterBoardSize
- size
) / 2;
225 int i
= 0; // index of grid
226 for (int r
= start
; r
< start
+size
; ++r
)
227 for (int c
= start
; c
< start
+size
; ++c
) {
228 int index
= r
* MasterBoardSize
+ c
; // index of board
229 board
[index
]->setDirs(grid
.cells()[i
]->cables());
230 board
[index
]->setConnected(false);
231 board
[index
]->setRoot(false);
232 board
[index
]->setLocked(false);
234 if (grid
.cells()[i
]->isServer()) {
235 board
[index
]->setConnected(true); // TODO: put in Cell::setRoot()
236 board
[index
]->setRoot(true);
242 const int start = (MasterBoardSize - size) / 2;
243 const int rootrow = rand() % size;
244 const int rootcol = rand() % size;
246 root = board[(start + rootrow) * MasterBoardSize + start + rootcol];
247 root->setConnected(true);
252 for (int row = start; row < start + size; row++)
253 for(int col = start; col < start + size; col++)
254 board[row * MasterBoardSize + col]->setDirs(None);
258 if (rand() % 2) addRandomDir(list);
260 while (!list.isEmpty())
265 if (rand() % 2) addRandomDir(list);
266 list.erase(list.begin());
270 list.append(list.first());
271 list.erase(list.begin());
276 for (int i = 0; i < MasterBoardSize * MasterBoardSize; i++)
278 Directions d = board[i]->dirs();
279 if ((d != None) && (d != Cell::None)) cells++;
281 if (cells >= MinimumNumCells) break;
284 for (int i = 0; i < MasterBoardSize * MasterBoardSize; i++)
285 board[i]->rotate((rand() % 4) * 90);
288 KGameDifficulty::setRunning(false); // setRunning(true) on the first click
291 void MainWindow::updateConnections()
293 bool newconnection
[MasterBoardSize
* MasterBoardSize
];
294 for (int i
= 0; i
< MasterBoardSize
* MasterBoardSize
; i
++) {
295 newconnection
[i
] = false;
299 if (!root
->isRotated()) {
300 newconnection
[root
->index()] = true;
304 while (!list
.isEmpty()) {
305 Cell
* cell
= list
.first();
306 Cell
* ucell
= uCell(cell
);
307 Cell
* rcell
= rCell(cell
);
308 Cell
* dcell
= dCell(cell
);
309 Cell
* lcell
= lCell(cell
);
311 if ((cell
->dirs() & Up
) && ucell
&& (ucell
->dirs() & Down
) &&
312 !newconnection
[ucell
->index()] && !ucell
->isRotated()) {
313 newconnection
[ucell
->index()] = true;
316 if ((cell
->dirs() & Right
) && rcell
&& (rcell
->dirs() & Left
) &&
317 !newconnection
[rcell
->index()] && !rcell
->isRotated()) {
318 newconnection
[rcell
->index()] = true;
321 if ((cell
->dirs() & Down
) && dcell
&& (dcell
->dirs() & Up
) &&
322 !newconnection
[dcell
->index()] && !dcell
->isRotated()) {
323 newconnection
[dcell
->index()] = true;
326 if ((cell
->dirs() & Left
) && lcell
&& (lcell
->dirs() & Right
) &&
327 !newconnection
[lcell
->index()] && !lcell
->isRotated()) {
328 newconnection
[lcell
->index()] = true;
331 list
.erase(list
.begin());
334 bool newConnections
= false;
335 for (int i
= 0; i
< MasterBoardSize
* MasterBoardSize
; i
++) {
336 if (!board
[i
]->isConnected() && newconnection
[i
]) {
337 newConnections
= true;
339 board
[i
]->setConnected(newconnection
[i
]);
342 if (newConnections
) checkIfGameEnded();
345 Cell
* MainWindow::uCell(Cell
* cell
) const
347 if (cell
->index() >= MasterBoardSize
) {
348 return board
[cell
->index() - MasterBoardSize
];
349 } else if (wrapped
) {
350 return board
[MasterBoardSize
* (MasterBoardSize
- 1) + cell
->index()];
356 Cell
* MainWindow::dCell(Cell
* cell
) const
358 if (cell
->index() < MasterBoardSize
* (MasterBoardSize
- 1)) {
359 return board
[cell
->index() + MasterBoardSize
];
360 } else if (wrapped
) {
361 return board
[cell
->index() - MasterBoardSize
* (MasterBoardSize
- 1)];
367 Cell
* MainWindow::lCell(Cell
* cell
) const
369 if (cell
->index() % MasterBoardSize
> 0) {
370 return board
[cell
->index() - 1];
371 } else if (wrapped
) {
372 return board
[cell
->index() - 1 + MasterBoardSize
];
378 Cell
* MainWindow::rCell(Cell
* cell
) const
380 if (cell
->index() % MasterBoardSize
< MasterBoardSize
- 1) {
381 return board
[cell
->index() + 1];
382 } else if (wrapped
) {
383 return board
[cell
->index() + 1 - MasterBoardSize
];
389 void MainWindow::lClicked(int index
)
391 KGameDifficulty::setRunning(true);
392 rotate(index
, false);
395 void MainWindow::rClicked(int index
)
397 KGameDifficulty::setRunning(true);
401 void MainWindow::mClicked(int index
)
403 KGameDifficulty::setRunning(true);
404 board
[index
]->setLocked( !board
[index
]->isLocked() );
407 void MainWindow::rotate(int index
, bool clockWise
)
409 const Directions d
= board
[index
]->dirs();
410 if ((d
== None
) || gameEnded
|| board
[index
]->isLocked()) {
411 KNotification::event( "clicksound" );
414 KNotification::event( "turnsound" );
416 board
[index
]->animateRotation(clockWise
);
418 // FIXME: won't work!!!
419 //if (updateConnections())
420 // KNotification::event( "connectsound" );
423 QString clicks
= i18n("Moves: %1",m_clickcount
);
424 statusBar()->changeItem(clicks
,1);
428 void MainWindow::blink(int index
)
430 for (int i
= 0; i
< board
[index
]->width() * 2; i
+= 2) {
431 qApp
->processEvents(QEventLoop::ExcludeUserInputEvents
);
432 QTimer::singleShot(30, board
[index
], SLOT(update()));
433 qApp
->processEvents(QEventLoop::ExcludeUserInputEvents
|
434 QEventLoop::WaitForMoreEvents
);
435 board
[index
]->setLight(i
);
438 board
[index
]->setLight(0);
441 void MainWindow::checkIfGameEnded()
444 for (int i
= 0; i
< MasterBoardSize
* MasterBoardSize
; i
++) {
445 const Directions d
= board
[i
]->dirs();
446 if ((d
!= None
) && !board
[i
]->isConnected())
451 KNotification::event( "winsound" );
454 KScoreDialog
ksdialog(KScoreDialog::Name
, this);
455 ksdialog
.setConfigGroup(KGameDifficulty::levelString());
457 //ksdialog.addField(KScoreDialog::Custom1, "Num of Moves", "moves");
458 //KScoreDialog::FieldInfo scoreInfo;
459 //scoreInfo[KScoreDialog::Score].setNum(1000 * boardSize() * boardSize() / m_clickcount);
460 //scoreInfo[KScoreDialog::Score].setNum(m_clickcount);
462 ksdialog
.addScore(m_clickcount
, KScoreDialog::LessIsMore
);
465 KGameDifficulty::setRunning(false);
470 int MainWindow::boardSize()
472 switch (KGameDifficulty::level()) {
473 case KGameDifficulty::Easy
: return NoviceBoardSize
;
474 case KGameDifficulty::Medium
: return NormalBoardSize
;
475 case KGameDifficulty::Hard
: return ExpertBoardSize
;
476 default: return MasterBoardSize
;
480 void MainWindow::closeEvent(QCloseEvent
* event
)
485 void MainWindow::configureNotifications()
487 KNotifyConfigWidget::configure(this);
490 void MainWindow::paintEvent(QPaintEvent
* e
)
492 if (e
->rect().intersects(centralWidget()->geometry())) {
494 QRect updateRect
= e
->rect().intersect(centralWidget()->geometry());
496 if (m_invalidCache
) {
497 m_invalidCache
= false;
500 int width
= centralWidget()->width();
501 int height
= centralWidget()->height();
504 pixmapCache
= new QPixmap(width
, height
);
506 Renderer::self()->backgroundPixmap(centralWidget()->size());
508 // calculate background overlay bounding rect
509 int size
= qMin(width
, height
);
512 size
= static_cast<int>(size
* (1.0 - 2*OverlayBorder
));
513 int borderLeft
= (width
- size
)/2;
514 int borderTop
= (height
- size
)/2;
516 QPixmap overlay
= Renderer::self()->backgroundOverlayPixmap(size
);
518 painter
.begin(pixmapCache
);
519 painter
.drawPixmap(borderLeft
, borderTop
, overlay
);
523 QPoint p
= centralWidget()->mapFromParent(QPoint(updateRect
.x(),
525 QRect
pixmapRect(p
.x(), p
.y(), updateRect
.width(), updateRect
.height());
528 painter
.drawPixmap(updateRect
, *pixmapCache
, pixmapRect
);
533 void MainWindow::resizeEvent(QResizeEvent
*)
535 m_invalidCache
= true;
538 #include "mainwindow.moc"