// qnetwalk/mainwindow.cpp // Copyright (C) 2004, Andi Peredri #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "cell.h" #include "mainwindow.h" static QMap contrdirs; MainWindow::MainWindow() : QMainWindow(0, 0, WStyle_DialogBorder) { contrdirs[Cell::U] = Cell::D; contrdirs[Cell::R] = Cell::L; contrdirs[Cell::D] = Cell::U; contrdirs[Cell::L] = Cell::R; QString appdir = qApp->applicationDirPath(); soundpath = appdir + "/sounds/"; if(!QFile::exists(soundpath)) soundpath = appdir + "/../share/qnetwalk/sounds/"; winsound = new QSound(soundpath + "win.wav"); turnsound = new QSound(soundpath + "turn.wav"); clicksound = new QSound(soundpath + "click.wav"); startsound = new QSound(soundpath + "start.wav"); connectsound = new QSound(soundpath + "connect.wav"); QSettings settings; settings.beginGroup("/QNetWalk"); username = settings.readEntry("/Username", getenv("USER")); bool issound = settings.readBoolEntry("/Sound", true); highscores = settings.readListEntry("/Highscores"); if(highscores.count() != NumHighscores * 8) { highscores.clear(); for(int i = 1; i < 5; i++) { for(int scores = 20 * i; scores < 30 * i; scores += i) { highscores.append("..."); highscores.append(QString::number(scores)); } } } skill = settings.readNumEntry("/Skill", Novice); if((skill != Novice) && (skill != Normal) && (skill != Expert)) skill = Master; for(int i = 1; i < qApp->argc(); i++) { QString argument = qApp->argv()[i]; if(argument == "-novice") skill = Novice; else if(argument == "-amateur") skill = Normal; else if(argument == "-expert") skill = Expert; else if(argument == "-master") skill = Master; else if(argument == "-nosound") issound = false; else qWarning("Unknown option: '" + argument + "'. Try -help."); } setCaption(tr("QNetWalk")); setIcon(QPixmap::fromMimeSource("qnetwalk.png")); setFixedSize(minimumSizeHint()); setDockWindowsMovable(false); setDockMenuEnabled(false); soundaction = new QAction(tr("&Sound"), CTRL+Key_S, this); soundaction->setToggleAction(true); soundaction->setOn(issound); gamemenu = new QPopupMenu(this); gamemenu->insertItem(QPixmap::fromMimeSource("newgame.png"), tr("&New"), this, SLOT(newGame()), CTRL+Key_N); gamemenu->insertItem(QPixmap::fromMimeSource("highscores.png"), tr("&Highscores"), this, SLOT(showHighscores()), CTRL+Key_H); soundaction->addTo(gamemenu); gamemenu->insertSeparator(); gamemenu->insertItem(QPixmap::fromMimeSource("quit.png"), tr("&Quit"), qApp, SLOT(closeAllWindows()), CTRL+Key_Q); skillmenu = new QPopupMenu(this); skillmenu->insertItem(tr("&Novice"), this, SLOT(setSkillNovice()), CTRL + Key_1, Novice); skillmenu->insertItem(tr("&Amateur"), this, SLOT(setSkillNormal()), CTRL + Key_2, Normal); skillmenu->insertItem(tr("&Expert"), this, SLOT(setSkillExpert()), CTRL + Key_3, Expert); skillmenu->insertItem(tr("&Master"), this, SLOT(setSkillMaster()), CTRL + Key_4, Master); QPopupMenu* helpmenu=new QPopupMenu(this); helpmenu->insertItem(tr("&Rules of Play"), this, SLOT(help()), Key_F1); helpmenu->insertItem(QPixmap::fromMimeSource("homepage.png"), tr("&Homepage"), this, SLOT(openHomepage())); helpmenu->insertSeparator(); helpmenu->insertItem(QPixmap::fromMimeSource("qnetwalk.png"), tr("&About QNetWalk"), this, SLOT(about()), CTRL+Key_A); helpmenu->insertItem(tr("About &Qt"), qApp, SLOT(aboutQt())); menuBar()->insertItem(tr("&Game"), gamemenu); menuBar()->insertItem(tr("&Skill"), skillmenu); menuBar()->insertItem(tr("&Help"), helpmenu); QToolBar* toolbar = new QToolBar(this); QSize buttonsize(30, 30); QToolButton* button; button = new QToolButton(QPixmap::fromMimeSource("newgame.png"), tr("New Game"), "", this, SLOT(newGame()), toolbar); button->setMinimumSize(buttonsize); button = new QToolButton(QPixmap::fromMimeSource("highscores.png"), tr("Show Highscores"), "", this, SLOT(showHighscores()), toolbar); button->setMinimumSize(buttonsize); lcd = new QLCDNumber(toolbar); lcd->setFrameStyle(QFrame::Plain); const int cellsize = QPixmap::fromMimeSource("background.png").width(); const int gridsize = cellsize * MasterBoardSize + 2; QGrid* grid = new QGrid(MasterBoardSize, this); grid->setFrameStyle(QFrame::Panel | QFrame::Sunken); grid->setFixedSize(gridsize, gridsize); setCentralWidget(grid); Cell::initPixmaps(); for(int i = 0; i < MasterBoardSize * MasterBoardSize; i++) { board[i] = new Cell(grid, i); board[i]->setFixedSize(cellsize, cellsize); connect(board[i], SIGNAL(lClicked(int)), SLOT(lClicked(int))); connect(board[i], SIGNAL(rClicked(int)), SLOT(rClicked(int))); } srand(time(0)); setSkill(skill); } void MainWindow::newGame() { lcd->display(0); if(soundaction->isOn()) startsound->play(); for(int i = 0; i < MasterBoardSize * MasterBoardSize; i++) { board[i]->setDirs(Cell::None); board[i]->setConnected(false); board[i]->setRoot(false); } const int size = (skill == Novice) ? NoviceBoardSize : (skill == Normal) ? NormalBoardSize : (skill == Expert) ? ExpertBoardSize : MasterBoardSize; const int start = (MasterBoardSize - size) / 2; const int rootrow = rand() % size; const int rootcol = rand() % size; root = board[(start + rootrow) * MasterBoardSize + start + rootcol]; root->setConnected(true); root->setRoot(true); while(true) { for(int row = start; row < start + size; row++) for(int col = start; col < start + size; col++) board[row * MasterBoardSize + col]->setDirs(Cell::Free); CellList list; list.append(root); if(rand() % 2) addRandomDir(list); while(!list.isEmpty()) { if(rand() % 2) { addRandomDir(list); if(rand() % 2) addRandomDir(list); list.remove(list.begin()); } else { list.append(list.first()); list.remove(list.begin()); } } int cells = 0; for(int i = 0; i < MasterBoardSize * MasterBoardSize; i++) { Cell::Dirs d = board[i]->dirs(); if((d != Cell::Free) && (d != Cell::None)) cells++; } if(cells >= MinimumNumCells) break; } for(int i = 0; i < MasterBoardSize * MasterBoardSize; i++) board[i]->rotate((rand() % 4) * 90); updateConnections(); } bool MainWindow::updateConnections() { bool newconnection[MasterBoardSize * MasterBoardSize]; for(int i = 0; i < MasterBoardSize * MasterBoardSize; i++) newconnection[i] = false; CellList list; if(!root->isRotated()) { newconnection[root->index()] = true; list.append(root); } while(!list.isEmpty()) { Cell* cell = list.first(); Cell* ucell = uCell(cell); Cell* rcell = rCell(cell); Cell* dcell = dCell(cell); Cell* lcell = lCell(cell); if((cell->dirs() & Cell::U) && ucell && (ucell->dirs() & Cell::D) && !newconnection[ucell->index()] && !ucell->isRotated()) { newconnection[ucell->index()] = true; list.append(ucell); } if((cell->dirs() & Cell::R) && rcell && (rcell->dirs() & Cell::L) && !newconnection[rcell->index()] && !rcell->isRotated()) { newconnection[rcell->index()] = true; list.append(rcell); } if((cell->dirs() & Cell::D) && dcell && (dcell->dirs() & Cell::U) && !newconnection[dcell->index()] && !dcell->isRotated()) { newconnection[dcell->index()] = true; list.append(dcell); } if((cell->dirs() & Cell::L) && lcell && (lcell->dirs() & Cell::R) && !newconnection[lcell->index()] && !lcell->isRotated()) { newconnection[lcell->index()] = true; list.append(lcell); } list.remove(list.begin()); } bool isnewconnection = false; for(int i = 0; i < MasterBoardSize * MasterBoardSize; i++) { if(!board[i]->isConnected() && newconnection[i]) isnewconnection = true; board[i]->setConnected(newconnection[i]); } return isnewconnection; } void MainWindow::addRandomDir(CellList& list) { Cell* cell = list.first(); Cell* ucell = uCell(cell); Cell* rcell = rCell(cell); Cell* dcell = dCell(cell); Cell* lcell = lCell(cell); typedef QMap CellMap; CellMap freecells; if(ucell && ucell->dirs() == Cell::Free) freecells[Cell::U] = ucell; if(rcell && rcell->dirs() == Cell::Free) freecells[Cell::R] = rcell; if(dcell && dcell->dirs() == Cell::Free) freecells[Cell::D] = dcell; if(lcell && lcell->dirs() == Cell::Free) freecells[Cell::L] = lcell; if(freecells.isEmpty()) return; CellMap::ConstIterator it = freecells.constBegin(); for(int i = rand() % freecells.count(); i > 0; --i) ++it; cell->setDirs(Cell::Dirs(cell->dirs() | it.key())); it.data()->setDirs(contrdirs[it.key()]); list.append(it.data()); } Cell* MainWindow::uCell(Cell* cell) const { if(cell->index() >= MasterBoardSize) return board[cell->index() - MasterBoardSize]; else if(wrapped) return board[MasterBoardSize * (MasterBoardSize - 1) + cell->index()]; else return 0; } Cell* MainWindow::dCell(Cell* cell) const { if(cell->index() < MasterBoardSize * (MasterBoardSize - 1)) return board[cell->index() + MasterBoardSize]; else if(wrapped) return board[cell->index() - MasterBoardSize * (MasterBoardSize - 1)]; else return 0; } Cell* MainWindow::lCell(Cell* cell) const { if(cell->index() % MasterBoardSize > 0) return board[cell->index() - 1]; else if(wrapped) return board[cell->index() - 1 + MasterBoardSize]; else return 0; } Cell* MainWindow::rCell(Cell* cell) const { if(cell->index() % MasterBoardSize < MasterBoardSize - 1) return board[cell->index() + 1]; else if(wrapped) return board[cell->index() + 1 - MasterBoardSize]; else return 0; } void MainWindow::lClicked(int index) { rotate(index, true); } void MainWindow::rClicked(int index) { rotate(index, false); } void MainWindow::rotate(int index, bool toleft) { const Cell::Dirs d = board[index]->dirs(); if((d == Cell::Free) || (d == Cell::None) || isGameOver()) { if(soundaction->isOn()) clicksound->play(); blink(index); } else { if(soundaction->isOn()) turnsound->play(); board[index]->rotate(toleft ? -6 : 6); updateConnections(); for(int i = 0; i < 14; i++) { qApp->eventLoop()->processEvents(QEventLoop::ExcludeUserInput); QTimer::singleShot(20, board[index], SLOT(update())); qApp->eventLoop()->processEvents(QEventLoop::ExcludeUserInput | QEventLoop::WaitForMore); board[index]->rotate(toleft ? -6 : 6); } if(updateConnections() && soundaction->isOn()) connectsound->play(); lcd->display(lcd->intValue() + 1); if(isGameOver()) { if(soundaction->isOn()) winsound->play(); blink(index); QStringList::ConstIterator it; it = highscores.at(2 * (skill + 1) * NumHighscores - 1); if(lcd->intValue() <= (*it).toInt()) addHighscore(lcd->intValue()); } } } void MainWindow::blink(int index) { for(int i = 0; i < board[index]->width() * 2; i += 2) { qApp->eventLoop()->processEvents(QEventLoop::ExcludeUserInput); QTimer::singleShot(20, board[index], SLOT(update())); qApp->eventLoop()->processEvents(QEventLoop::ExcludeUserInput | QEventLoop::WaitForMore); board[index]->setLight(i); } board[index]->setLight(0); } bool MainWindow::isGameOver() { for(int i = 0; i < MasterBoardSize * MasterBoardSize; i++) { const Cell::Dirs d = board[i]->dirs(); if((d != Cell::Free) && (d != Cell::None) && !board[i]->isConnected()) return false; } return true; } void MainWindow::showHighscores() { addHighscore(0); } void MainWindow::addHighscore(int score) { QDialog* dialog = new QDialog(this); dialog->setCaption(tr("Highscores")); QGridLayout* grid = new QGridLayout(dialog, NumHighscores + 5, 4, 10, 5); grid->setColStretch(2, 1); grid->setRowStretch(NumHighscores + 3, 1); grid->setColSpacing(0, 40); grid->setColSpacing(2, 150); QLabel* label = new QLabel(dialog); label->setPixmap(QPixmap::fromMimeSource("computer2.png")); grid->addMultiCellWidget(label, 0, 1, 0, 0); label = new QLabel(tr("

Highscores"), dialog); grid->addMultiCellWidget(label, 0, 0, 1, 3); const QString header = (skill == Novice) ? tr("Novices") : (skill == Normal) ? tr("Amateurs") : (skill == Expert) ? tr("Experts") : tr("Masters"); grid->addWidget(new QLabel("#", dialog), 1, 1); grid->addWidget(new QLabel("" + header, dialog), 1, 2); grid->addWidget(new QLabel(tr("Scores"), dialog), 1, 3); QFrame* frame = new QFrame(dialog); frame->setFrameStyle(QFrame::HLine | QFrame::Sunken); grid->addMultiCellWidget(frame, 2, 2, 1, 3); QLineEdit* line = 0; QStringList::Iterator inserted; QStringList::Iterator it = highscores.at(2 * skill * NumHighscores); for(unsigned int i = 0; i < NumHighscores; i++) { label = new QLabel(QString::number(i + 1), dialog); grid->addWidget(label, i + 3, 1); QStringList::Iterator next = it; if((score > 0) && (score <= (*(++next)).toInt()) && !line) { inserted = it; line = new QLineEdit(username, dialog); grid->addWidget(line, i + 3, 2); label = new QLabel(QString::number(score), dialog); grid->addWidget(label, i + 3, 3); } else { grid->addWidget(new QLabel(*(it++), dialog), i + 3, 2); grid->addWidget(new QLabel(*(it++), dialog), i + 3, 3); } } QPushButton* button = new QPushButton("OK", dialog); connect(button, SIGNAL(clicked()), dialog, SLOT(accept())); const int pos = NumHighscores + 4; grid->addMultiCellWidget(button, pos, pos, 0, 3, AlignHCenter); dialog->exec(); if(line) { username = line->text(); highscores.insert(inserted, username); highscores.insert(inserted, QString::number(score)); highscores.erase(highscores.erase(it)); } delete dialog; } void MainWindow::setSkill(int s) { if(skillmenu->isItemChecked(s)) return; skillmenu->setItemChecked(skill, false); skill = s; skillmenu->setItemChecked(skill, true); if(skill == Master) wrapped = true; else wrapped = false; newGame(); } void MainWindow::closeEvent(QCloseEvent* event) { QSettings settings; settings.beginGroup("/QNetWalk"); settings.writeEntry("/Skill", skill); settings.writeEntry("/Username", username); settings.writeEntry("/Highscores", highscores); settings.writeEntry("/Sound", soundaction->isOn()); event->accept(); } void MainWindow::openHomepage() { if(!startBrowser("http://qt.osdn.org.ua/qnetwalk.html")) QMessageBox::warning(this, tr("Error"), tr("Could not launch your web browser.\n" "Please, check the BROWSER environment's variable.")); } bool MainWindow::startBrowser(const QString& url) { QStringList browsers; QString env = getenv("BROWSER"); if(!env.isEmpty()) browsers << env; browsers << "konqueror" << "mozilla" << "opera" << "netscape"; QProcess process; while(!browsers.isEmpty()) { process.clearArguments(); process.addArgument(browsers.first()); process.addArgument(url); if(process.start()) return true; browsers.remove(browsers.begin()); } return false; } void MainWindow::help() { QMessageBox box(this); box.setCaption(tr("Rules of Play")); box.setIconPixmap(QPixmap::fromMimeSource("computer2.png")); box.setText(tr("

Rules of Play

" "

You are the system administrator and your goal" " is to connect each computer to the central server." "

Click the right mouse's button for turning the cable" " in a clockwise direction, and left mouse's button" " for turning the cable in a counter-clockwise direction." "

Start the LAN with as few turns as possible!")); box.exec(); } void MainWindow::about() { QMessageBox box(this); box.setCaption(tr("About QNetWalk")); box.setIconPixmap(QPixmap::fromMimeSource("computer2.png")); box.setText(tr("

About QNetWalk 1.2

" "

QNetWalk is a free Qt-version of the NetWalk game." "

Copyright (C) 2004, Andi Peredri" "

Homepage: http://qt.osdn.org.ua/qnetwalk.html" "

This program is distributed under the terms of the" " GNU General Public License.")); box.exec(); }