|
|
|
/***************************************************************************
|
|
|
|
* Copyright (C) 2004, 2005 Andi Peredri *
|
|
|
|
* andi@ukr.net *
|
|
|
|
* *
|
|
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
|
|
* it under the terms of the GNU General Public License version 2 *
|
|
|
|
* as published by the Free Software Foundation (see COPYING) *
|
|
|
|
* *
|
|
|
|
* This program is distributed in the hope that it will be useful, *
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
|
|
* GNU General Public License for more details. *
|
|
|
|
***************************************************************************/
|
|
|
|
|
|
|
|
#include <tqaction.h>
|
|
|
|
#include <tqapplication.h>
|
|
|
|
#include <tqeventloop.h>
|
|
|
|
#include <tqgrid.h>
|
|
|
|
#include <tqlabel.h>
|
|
|
|
#include <tqlayout.h>
|
|
|
|
#include <tqlineedit.h>
|
|
|
|
#include <tqprocess.h>
|
|
|
|
#include <tqpushbutton.h>
|
|
|
|
#include <tqsettings.h>
|
|
|
|
#include <tqsound.h>
|
|
|
|
#include <tqtimer.h>
|
|
|
|
#include <tqtoolbutton.h>
|
|
|
|
#include <tqwhatsthis.h>
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <time.h>
|
|
|
|
|
|
|
|
#include <kglobal.h>
|
|
|
|
#include <kiconloader.h>
|
|
|
|
#include <klocale.h>
|
|
|
|
#include <kscoredialog.h>
|
|
|
|
#include <khighscore.h>
|
|
|
|
#include <kstdaction.h>
|
|
|
|
#include <kaction.h>
|
|
|
|
#include <kstdgameaction.h>
|
|
|
|
#include <kmenubar.h>
|
|
|
|
#include <kstatusbar.h>
|
|
|
|
#include <kdebug.h>
|
|
|
|
#include <knotifyclient.h>
|
|
|
|
#include <knotifydialog.h>
|
|
|
|
#include <kexthighscore.h>
|
|
|
|
|
|
|
|
#include "settings.h"
|
|
|
|
#include "cell.h"
|
|
|
|
#include "mainwindow.h"
|
|
|
|
|
|
|
|
static TQMap<Cell::Dirs, Cell::Dirs> contrdirs;
|
|
|
|
|
|
|
|
MainWindow::MainWindow(TQWidget *parent, const char* name, WFlags /*fl*/) :
|
|
|
|
KMainWindow(parent, name, WStyle_NoBorder)
|
|
|
|
{
|
|
|
|
m_clickcount = 0;
|
|
|
|
|
|
|
|
contrdirs[Cell::U] = Cell::D;
|
|
|
|
contrdirs[Cell::R] = Cell::L;
|
|
|
|
contrdirs[Cell::D] = Cell::U;
|
|
|
|
contrdirs[Cell::L] = Cell::R;
|
|
|
|
|
|
|
|
KNotifyClient::startDaemon();
|
|
|
|
|
|
|
|
KStdGameAction::gameNew(TQT_TQOBJECT(this), TQT_SLOT(slotNewGame()), actionCollection());
|
|
|
|
|
|
|
|
KStdGameAction::highscores(TQT_TQOBJECT(this), TQT_SLOT(showHighscores()), actionCollection());
|
|
|
|
KStdGameAction::quit(TQT_TQOBJECT(this), TQT_SLOT(close()), actionCollection());
|
|
|
|
KStdGameAction::configureHighscores(TQT_TQOBJECT(this), TQT_SLOT(configureHighscores()), actionCollection());
|
|
|
|
|
|
|
|
m_levels = KStdGameAction::chooseGameType(0, 0, actionCollection());
|
|
|
|
TQStringList lst;
|
|
|
|
lst += i18n("Novice");
|
|
|
|
lst += i18n("Normal");
|
|
|
|
lst += i18n("Expert");
|
|
|
|
lst += i18n("Master");
|
|
|
|
m_levels->setItems(lst);
|
|
|
|
|
|
|
|
setFixedSize(minimumSizeHint());
|
|
|
|
|
|
|
|
statusBar()->insertItem("abcdefghijklmnopqrst: 0 ",1);
|
|
|
|
setAutoSaveSettings();
|
|
|
|
createGUI();
|
|
|
|
connect(m_levels, TQT_SIGNAL(activated(int)), this, TQT_SLOT(newGame(int)));
|
|
|
|
|
|
|
|
|
|
|
|
TQWhatsThis::add(this, i18n("<h3>Rules of the Game</h3>"
|
|
|
|
"<p>You are the system administrator and your goal"
|
|
|
|
" is to connect each computer to the central server."
|
|
|
|
"<p>Click the right mouse button to turn the cable"
|
|
|
|
" in a clockwise direction, and the left mouse button"
|
|
|
|
" to turn it in a counter-clockwise direction."
|
|
|
|
"<p>Start the LAN with as few turns as possible!"));
|
|
|
|
|
|
|
|
//const int cellsize = TDEGlobal::iconLoader()->loadIcon("knetwalk/background.png", KIcon::User, 32).width();
|
|
|
|
const int cellsize = 32;
|
|
|
|
const int gridsize = cellsize * MasterBoardSize + 2;
|
|
|
|
|
|
|
|
TQGrid* grid = new TQGrid(MasterBoardSize, this);
|
|
|
|
grid->setFrameStyle(TQFrame::Panel | TQFrame::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], TQT_SIGNAL(lClicked(int)), TQT_SLOT(lClicked(int)));
|
|
|
|
connect(board[i], TQT_SIGNAL(rClicked(int)), TQT_SLOT(rClicked(int)));
|
|
|
|
connect(board[i], TQT_SIGNAL(mClicked(int)), TQT_SLOT(mClicked(int)));
|
|
|
|
}
|
|
|
|
srand(time(0));
|
|
|
|
|
|
|
|
slotNewGame();
|
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::configureHighscores()
|
|
|
|
{
|
|
|
|
KExtHighscore::configure(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::showHighscores()
|
|
|
|
{
|
|
|
|
KExtHighscore::show(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::slotNewGame()
|
|
|
|
{
|
|
|
|
newGame( Settings::skill() );
|
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::newGame(int sk)
|
|
|
|
{
|
|
|
|
if (sk==Settings::EnumSkill::Novice || sk==Settings::EnumSkill::Normal
|
|
|
|
|| sk==Settings::EnumSkill::Expert || sk==Settings::EnumSkill::Master)
|
|
|
|
{
|
|
|
|
Settings::setSkill(sk);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(Settings::skill() == Settings::EnumSkill::Master) wrapped = true;
|
|
|
|
else wrapped = false;
|
|
|
|
|
|
|
|
KExtHighscore::setGameType(Settings::skill());
|
|
|
|
|
|
|
|
Settings::writeConfig();
|
|
|
|
|
|
|
|
m_clickcount = 0;
|
|
|
|
TQString clicks = i18n("Click: %1");
|
|
|
|
statusBar()->changeItem(clicks.arg(TQString::number(m_clickcount)),1);
|
|
|
|
|
|
|
|
KNotifyClient::event(winId(), "startsound", i18n("New Game"));
|
|
|
|
for(int i = 0; i < MasterBoardSize * MasterBoardSize; i++)
|
|
|
|
{
|
|
|
|
board[i]->setDirs(Cell::None);
|
|
|
|
board[i]->setConnected(false);
|
|
|
|
board[i]->setRoot(false);
|
|
|
|
board[i]->setLocked(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
const int size = (Settings::skill() == Settings::EnumSkill::Novice) ? NoviceBoardSize :
|
|
|
|
(Settings::skill() == Settings::EnumSkill::Normal) ? NormalBoardSize :
|
|
|
|
(Settings::skill() == Settings::EnumSkill::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 TQMap<Cell::Dirs, Cell*> 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::mClicked(int index)
|
|
|
|
{
|
|
|
|
board[index]->setLocked( !board[index]->isLocked() );
|
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::rotate(int index, bool toleft)
|
|
|
|
{
|
|
|
|
const Cell::Dirs d = board[index]->dirs();
|
|
|
|
if((d == Cell::Free) || (d == Cell::None) || isGameOver() || board[index]->isLocked() )
|
|
|
|
{
|
|
|
|
KNotifyClient::event(winId(), "clicksound");
|
|
|
|
blink(index);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
KNotifyClient::event(winId(), "turnsound");
|
|
|
|
board[index]->rotate(toleft ? -6 : 6);
|
|
|
|
updateConnections();
|
|
|
|
for(int i = 0; i < 14; i++)
|
|
|
|
{
|
|
|
|
kapp->eventLoop()->processEvents(TQEventLoop::ExcludeUserInput);
|
|
|
|
TQTimer::singleShot(20, board[index], TQT_SLOT(update()));
|
|
|
|
kapp->eventLoop()->processEvents(TQEventLoop::ExcludeUserInput | TQEventLoop::WaitForMore);
|
|
|
|
board[index]->rotate(toleft ? -6 : 6);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (updateConnections())
|
|
|
|
KNotifyClient::event(winId(), "connectsound");
|
|
|
|
|
|
|
|
m_clickcount++;
|
|
|
|
TQString clicks = i18n("Click: %1");
|
|
|
|
statusBar()->changeItem(clicks.arg(TQString::number(m_clickcount)),1);
|
|
|
|
|
|
|
|
if (isGameOver())
|
|
|
|
{
|
|
|
|
KNotifyClient::event(winId(), "winsound");
|
|
|
|
blink(index);
|
|
|
|
|
|
|
|
KExtHighscore::Score score(KExtHighscore::Won);
|
|
|
|
score.setScore(m_clickcount);
|
|
|
|
KExtHighscore::submitScore(score, this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::blink(int index)
|
|
|
|
{
|
|
|
|
for(int i = 0; i < board[index]->width() * 2; i += 2)
|
|
|
|
{
|
|
|
|
kapp->eventLoop()->processEvents(TQEventLoop::ExcludeUserInput);
|
|
|
|
TQTimer::singleShot(20, board[index], TQT_SLOT(update()));
|
|
|
|
kapp->eventLoop()->processEvents(TQEventLoop::ExcludeUserInput |
|
|
|
|
TQEventLoop::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::closeEvent(TQCloseEvent* event)
|
|
|
|
{
|
|
|
|
event->accept();
|
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::configureNotifications()
|
|
|
|
{
|
|
|
|
KNotifyDialog::configure(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
#include "mainwindow.moc"
|