00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "boardwidget.h"
00021 #include "prefs.h"
00022
00023 #include <kmessagebox.h>
00024 #include <krandom.h>
00025 #include <QTimer>
00026 #include <qpainter.h>
00027 #include <klocale.h>
00028 #include <kstandarddirs.h>
00029 #include <qapplication.h>
00030 #include <kconfig.h>
00031 #include <kglobal.h>
00032 #include <KDebug>
00033
00038 BoardWidget::BoardWidget( QWidget* parent )
00039 : KGameCanvasWidget( parent ), theTiles()
00040 {
00041 QPalette palette;
00042 palette.setColor( backgroundRole(), Qt::black );
00043 setPalette(palette);
00044
00045 timer = new QTimer(this);
00046 connect( timer, SIGNAL(timeout()),
00047 this, SLOT(helpMoveTimeout()) );
00048
00049 TimerState = Stop;
00050 gamePaused = false;
00051 iTimerStep = 0;
00052 matchCount = 0;
00053 showMatch = false;
00054 showHelp = false;
00055
00056
00057 gameGenerationNum = 0;
00058 m_angle = NE;
00059
00060
00061 loadBoardLayout(Prefs::layout());
00062
00063
00064 Game = new GameData(theBoardLayout.board());
00065
00066 MouseClickPos1.e = Game->m_depth;
00067 MouseClickPos2.e = Game->m_depth;
00068
00069
00070 animateForwardTimer = new QTimer(this);
00071 animateForwardTimer->setSingleShot(true);
00072 animateForwardTimer->setInterval(100);
00073 connect(animateForwardTimer, SIGNAL(timeout()), SLOT(animatingMoveListForward()));
00074 animateForwardTimer->stop();
00075
00076 animateBackwardsTimer = new QTimer(this);
00077 animateBackwardsTimer->setSingleShot(true);
00078 animateBackwardsTimer->setInterval(100);
00079 connect(animateBackwardsTimer, SIGNAL(timeout()), SLOT(animatingMoveListBackwards()));
00080 animateBackwardsTimer->stop();
00081
00082 loadSettings();
00083 }
00084
00085 BoardWidget::~BoardWidget(){
00086 if (Game) delete Game;
00087 }
00088
00089 void BoardWidget::loadSettings(){
00090
00091
00092 if (!loadTileset(Prefs::tileSet())){
00093 kDebug() << "An error occurred when loading the tileset" << Prefs::tileSet() <<"KMahjongg will continue with the default tileset.";
00094 }
00095
00096
00097 if( ! loadBackground(Prefs::background(), false ) )
00098 {
00099 kDebug() << "An error occurred when loading the background" << Prefs::background() <<"KMahjongg will continue with the default background.";
00100 }
00101 setShowMatch( Prefs::showMatchingTiles() );
00102
00103 if (QString::compare(Prefs::layout(), theBoardLayout.path(), Qt::CaseSensitive)!=0) {
00104
00105 loadBoardLayout(Prefs::layout());
00106 calculateNewGame();
00107 }
00108 setDisplayedWidth();
00109 drawBoard(true);
00110
00111 saveSettings();
00112 }
00113
00114 void BoardWidget::resizeEvent ( QResizeEvent * event )
00115 {
00116 if (event->spontaneous()) return;
00117 resizeTileset(event->size());
00118 theBackground.sizeChanged(requiredWidth(), requiredHeight());
00119 drawBoard(true);
00120 }
00121
00122 void BoardWidget::resizeTileset ( const QSize & wsize )
00123 {
00124 QSize newtiles = theTiles.preferredTileSize(wsize, requiredHorizontalCells(), requiredVerticalCells());
00125
00126 theTiles.reloadTileset(newtiles);
00127 stopMatchAnimation();
00128 }
00129
00130
00131
00132 void BoardWidget::saveSettings(){
00133 Prefs::setTileSet(theTiles.path());
00134 Prefs::setLayout(theBoardLayout.path());
00135 Prefs::setBackground(theBackground.path());
00136 Prefs::self()->writeConfig();
00137 }
00138
00139 void BoardWidget::setDisplayedWidth() {
00140
00141
00142 resize(width() , height());
00143 }
00144
00145 void BoardWidget::populateSpriteMap() {
00146
00147 while (!items()->isEmpty())
00148 delete items()->first();
00149
00150
00151 spriteMap.clear();
00152
00153
00154 QPalette palette;
00155 palette.setBrush( backgroundRole(), theBackground.getBackground() );
00156 setPalette( palette );
00157 setAutoFillBackground (true);
00158
00159
00160 for (int z=0; z<Game->m_depth; z++) {
00161
00162 for (int y = 0; y < Game->m_height; y++) {
00163
00164 for (int x=Game->m_width-1; x>=0; x--) {
00165
00166
00167 if (!Game->tilePresent(z,y,x))
00168 continue;
00169
00170 QPixmap s;
00171 QPixmap us;
00172 QPixmap f;
00173 bool selected = false;
00174 s= theTiles.selectedTile(m_angle);
00175 us= theTiles.unselectedTile(m_angle);
00176 f= theTiles.tileface(Game->BoardData(z,y,x)-TILE_OFFSET);
00177 if (Game->HighlightData(z,y,x)) {
00178 selected = true;
00179 }
00180
00181 TileSprite * thissprite = new TileSprite(this, us, s, f, m_angle, selected);
00182
00183 spriteMap.insert(TileCoord(x,y,z), thissprite);
00184 }
00185 }
00186 }
00187
00188 updateSpriteMap();
00189 }
00190
00191
00192 void BoardWidget::updateSpriteMap() {
00193
00194
00195 int xOffset = (width() - (Game->m_width*(theTiles.qWidth())) - (theTiles.width()-(theTiles.qWidth()*2)))/2;;
00196 int yOffset = (height() - (Game->m_height*(theTiles.qHeight())) - (theTiles.height()-(theTiles.qHeight()*2)))/2;;
00197
00198
00199
00200
00201 switch (m_angle)
00202 {
00203 case NW:
00204
00205 xOffset += theTiles.levelOffsetX()/2;
00206 yOffset += theTiles.levelOffsetY()/2;
00207
00208
00209 for (int z=0; z<Game->m_depth; z++) {
00210
00211 for (int y = 0; y < Game->m_height; y++) {
00212
00213 for (int x=Game->m_width-1; x>=0; x--) {
00214 int sx = x*(theTiles.qWidth() )+xOffset;
00215 int sy = y*(theTiles.qHeight() )+yOffset;
00216
00217
00218 if (!Game->tilePresent(z,y,x))
00219 continue;
00220
00221 TileSprite * thissprite =spriteMap.value(TileCoord(x,y,z));
00222
00223 if (thissprite) thissprite->moveTo(sx, sy);
00224 if (thissprite) thissprite->show();
00225 }
00226 }
00227 xOffset +=theTiles.levelOffsetX();
00228 yOffset -=theTiles.levelOffsetY();
00229 }
00230
00231 for (int z=0; z<Game->m_depth; z++) {
00232
00233
00234
00235 for (int x=Game->m_width*2; x>=0; x--) {
00236
00237 int offset = 0;
00238 for (int y=Game->m_height-1; y>=0; y--) {
00239 if (Game->tilePresent(z,y,x-offset))
00240 {
00241 TileSprite * thissprite =spriteMap.value(TileCoord(x-offset,y,z));
00242 if (thissprite) thissprite->raise();
00243 }
00244
00245 offset++;
00246 }
00247 }
00248 }
00249 break;
00250
00251 case NE:
00252 xOffset -= theTiles.levelOffsetX()/2;
00253 yOffset += theTiles.levelOffsetY()/2;
00254
00255 for (int z=0; z<Game->m_depth; z++) {
00256
00257 for (int y = 0; y < Game->m_height; y++) {
00258
00259 for (int x=0; x<=Game->m_width-1; x++) {
00260 int sx = x*(theTiles.qWidth() )+xOffset;
00261 int sy = y*(theTiles.qHeight() )+yOffset;
00262
00263
00264 if (!Game->tilePresent(z,y,x))
00265 continue;
00266
00267 TileSprite * thissprite =spriteMap.value(TileCoord(x,y,z));
00268
00269 if (thissprite) thissprite->moveTo(sx, sy);
00270 if (thissprite) thissprite->show();
00271 }
00272 }
00273 xOffset -=theTiles.levelOffsetX();
00274 yOffset -=theTiles.levelOffsetY();
00275 }
00276
00277 for (int z=0; z<Game->m_depth; z++) {
00278
00279
00280
00281 for (int x=-(Game->m_width); x<=Game->m_width-1; x++) {
00282
00283 int offset = 0;
00284 for (int y=Game->m_height-1; y>=0; y--) {
00285 if (Game->tilePresent(z,y,x+offset))
00286 {
00287 TileSprite * thissprite =spriteMap.value(TileCoord(x+offset,y,z));
00288 if (thissprite) thissprite->raise();
00289 }
00290
00291 offset++;
00292 }
00293 }
00294 }
00295 break;
00296
00297 case SE:
00298 xOffset -= theTiles.levelOffsetX()/2;
00299 yOffset -= theTiles.levelOffsetY()/2;
00300
00301 for (int z=0; z<Game->m_depth; z++) {
00302 for (int y = Game->m_height-1; y >= 0; y--) {
00303 for (int x=0; x<=Game->m_width-1; x++) {
00304 int sx = x*(theTiles.qWidth() )+xOffset;
00305 int sy = y*(theTiles.qHeight() )+yOffset;
00306
00307 if (!Game->tilePresent(z,y,x))
00308 continue;
00309
00310 TileSprite * thissprite =spriteMap.value(TileCoord(x,y,z));
00311
00312 if (thissprite) thissprite->moveTo(sx, sy);
00313 if (thissprite) thissprite->show();
00314 }
00315 }
00316 xOffset -=theTiles.levelOffsetX();
00317 yOffset +=theTiles.levelOffsetY();
00318 }
00319
00320 for (int z=0; z<Game->m_depth; z++) {
00321 for (int x=-(Game->m_width); x<=Game->m_width-1; x++) {
00322 int offset = 0;
00323 for (int y=0; y<Game->m_height; y++) {
00324 if (Game->tilePresent(z,y,x+offset))
00325 {
00326 TileSprite * thissprite =spriteMap.value(TileCoord(x+offset,y,z));
00327 if (thissprite) thissprite->raise();
00328 }
00329 offset++;
00330 }
00331 }
00332 }
00333 break;
00334
00335 case SW:
00336 xOffset += theTiles.levelOffsetX()/2;
00337 yOffset -= theTiles.levelOffsetY()/2;
00338
00339
00340 for (int z=0; z<Game->m_depth; z++) {
00341 for (int y = Game->m_height-1; y >= 0; y--) {
00342 for (int x=Game->m_width-1; x>=0; x--) {
00343 int sx = x*(theTiles.qWidth() )+xOffset;
00344 int sy = y*(theTiles.qHeight() )+yOffset;
00345
00346 if (!Game->tilePresent(z,y,x))
00347 continue;
00348
00349 TileSprite * thissprite =spriteMap.value(TileCoord(x,y,z));
00350
00351 if (thissprite) thissprite->moveTo(sx, sy);
00352 if (thissprite) thissprite->show();
00353 }
00354 }
00355 xOffset +=theTiles.levelOffsetX();
00356 yOffset +=theTiles.levelOffsetY();
00357 }
00358
00359 for (int z=0; z<Game->m_depth; z++) {
00360 for (int x=Game->m_width*2; x>=0; x--) {
00361 int offset = 0;
00362 for (int y=0; y<Game->m_height; y++) {
00363 if (Game->tilePresent(z,y,x-offset))
00364 {
00365 TileSprite * thissprite =spriteMap.value(TileCoord(x-offset,y,z));
00366 if (thissprite) thissprite->raise();
00367 }
00368 offset++;
00369 }
00370 }
00371 }
00372 break;
00373 }
00374 }
00375
00376 void BoardWidget::pause() {
00377 gamePaused = !gamePaused;
00378 drawBoard(!gamePaused);
00379 }
00380
00381 void BoardWidget::gameLoaded()
00382 {
00383 int i;
00384 Game->initialiseRemovedTiles();
00385 i = Game->TileNum;
00386
00387 while (i < Game->MaxTileNum )
00388 {
00389 Game->setRemovedTilePair(Game->MoveListData(i), Game->MoveListData(i+1));
00390 i +=2;
00391 }
00392 populateSpriteMap();
00393 drawBoard(true);
00394 }
00395
00396
00397 int BoardWidget::undoMove()
00398 {
00399 cancelUserSelectedTiles();
00400
00401 if( Game->TileNum < Game->MaxTileNum )
00402 {
00403
00404 Game->clearRemovedTilePair(Game->MoveListData(Game->TileNum), Game->MoveListData(Game->TileNum+1));
00405 putTileInBoard( Game->MoveListData(Game->TileNum), false );
00406 Game->TileNum++;
00407 putTileInBoard( Game->MoveListData(Game->TileNum) );
00408 Game->TileNum++;
00409 drawTileNumber();
00410 setStatusText( i18n("Undo operation done successfully.") );
00411 return 1;
00412 }
00413 else {
00414 setStatusText(i18n("What do you want to undo? You have done nothing!"));
00415 return 0;
00416 }
00417 }
00418
00419
00420 void BoardWidget::helpMove()
00421 {
00422 cancelUserSelectedTiles();
00423 if (showHelp) helpMoveStop();
00424
00425 if( Game->findMove( TimerPos1, TimerPos2 ) )
00426 {
00427 cheatsUsed++;
00428 iTimerStep = 1;
00429 showHelp = true;
00430 helpMoveTimeout();
00431 }
00432 else
00433 setStatusText( i18n("Sorry, you have lost the game.") );
00434 }
00435
00436 void BoardWidget::helpMoveTimeout()
00437 {
00438 if( iTimerStep & 1 )
00439 {
00440 hilightTile( TimerPos1, true, false );
00441 hilightTile( TimerPos2, true );
00442 }
00443 else
00444 {
00445 hilightTile( TimerPos1, false, false );
00446 hilightTile( TimerPos2, false );
00447 }
00448
00449 if( iTimerStep++ < 8 )
00450 {
00451 timer->setSingleShot(true);
00452 timer->start( ANIMSPEED );
00453 }
00454 else
00455 showHelp = false;
00456 }
00457
00458
00459 void BoardWidget::helpMoveStop()
00460 {
00461 timer->stop();
00462 iTimerStep = 8;
00463 hilightTile( TimerPos1, false, false );
00464 hilightTile( TimerPos2, false );
00465 showHelp = false;
00466 }
00467
00468
00469 void BoardWidget::startDemoMode()
00470 {
00471 calculateNewGame();
00472
00473 if( TimerState == Stop )
00474 {
00475 TimerState = Demo;
00476 iTimerStep = 0;
00477 emit demoModeChanged( true );
00478 setStatusText( i18n("Demo mode. Click mousebutton to stop.") );
00479 demoMoveTimeout();
00480 }
00481 }
00482
00483 void BoardWidget::stopDemoMode()
00484 {
00485 TimerState = Stop;
00486 calculateNewGame();
00487 setStatusText( i18n("Now it is you again.") );
00488 emit demoModeChanged( false );
00489 emit gameCalculated();
00490 }
00491
00492 void BoardWidget::demoMoveTimeout()
00493 {
00494 if( TimerState == Demo )
00495 {
00496 switch( iTimerStep++ % 6 )
00497 {
00498
00499 case 0:
00500 if( ! Game->findMove( TimerPos1, TimerPos2 ) )
00501 {
00502
00503 if( Game->TileNum == 0 )
00504 {
00505 animateMoveList();
00506 }
00507
00508 else
00509 {
00510 setStatusText( i18n("Your computer has lost the game.") );
00511 while( Game->TileNum < Game->MaxTileNum )
00512 {
00513 putTileInBoard( Game->MoveListData(Game->TileNum), false );
00514 Game->TileNum++;
00515 putTileInBoard( Game->MoveListData(Game->TileNum) );
00516 Game->TileNum++;
00517 drawTileNumber();
00518 }
00519 }
00520 TimerState = Stop;
00521
00522
00523 stopDemoMode();
00524 return;
00525 }
00526 break;
00527
00528 case 1:
00529 case 3:
00530 hilightTile( TimerPos1, true, false );
00531 hilightTile( TimerPos2, true );
00532 break;
00533
00534 case 2:
00535 case 4:
00536 hilightTile( TimerPos1, false, false );
00537 hilightTile( TimerPos2, false );
00538 break;
00539
00540 case 5:
00541 Game->setRemovedTilePair(TimerPos1, TimerPos2);
00542 removeTile( TimerPos1, false );
00543 removeTile( TimerPos2 );
00544 drawTileNumber();
00545 break;
00546 }
00547
00548 QTimer::singleShot( ANIMSPEED, this, SLOT( demoMoveTimeout() ) );
00549 }
00550 }
00551
00552
00553 void BoardWidget::setShowMatch( bool show )
00554 {
00555 if( showMatch )
00556 stopMatchAnimation();
00557 showMatch = show;
00558 }
00559
00560 void BoardWidget::matchAnimationTimeout()
00561 {
00562 if (matchCount == 0)
00563 return;
00564
00565 if( iTimerStep++ & 1 )
00566 {
00567 for(short Pos = 0; Pos < matchCount; Pos++)
00568 {
00569 hilightTile(Game->getFromPosTable(Pos), true);
00570 }
00571 }
00572 else
00573 {
00574 for(short Pos = 0; Pos < matchCount; Pos++)
00575 {
00576 hilightTile(Game->getFromPosTable(Pos), false);
00577 }
00578 }
00579 if( TimerState == Match )
00580 QTimer::singleShot( ANIMSPEED, this, SLOT( matchAnimationTimeout() ) );
00581 }
00582
00583 void BoardWidget::stopMatchAnimation()
00584 {
00585 for(short Pos = 0; Pos < matchCount; Pos++)
00586 {
00587 hilightTile(Game->getFromPosTable(Pos), false);
00588 }
00589 TimerState = Stop;
00590 matchCount = 0;
00591 }
00592
00593 void BoardWidget::redoMove()
00594 {
00595
00596 Game->setRemovedTilePair(Game->MoveListData(Game->TileNum-1),Game->MoveListData(Game->TileNum-2));
00597 removeTile(Game->MoveListData(Game->TileNum-1), false);
00598 removeTile(Game->MoveListData(Game->TileNum-1));
00599 drawTileNumber();
00600 }
00601
00602
00603 void BoardWidget::animateMoveList()
00604 {
00605 setStatusText( i18n("Congratulations. You have won!") );
00606 animatingMoveListForward();
00607 }
00608
00609 void BoardWidget::animatingMoveListForward()
00610 {
00611 if (Game->TileNum < Game->MaxTileNum) {
00612
00613 putTileInBoard(Game->MoveListData(Game->TileNum));
00614 Game->TileNum++;
00615 putTileInBoard(Game->MoveListData(Game->TileNum), false);
00616 Game->TileNum++;
00617 drawTileNumber();
00618 animateForwardTimer->start();
00619 } else {
00620
00621 animateBackwardsTimer->start();
00622 }
00623 }
00624
00625 void BoardWidget::animatingMoveListBackwards()
00626 {
00627 if (Game->TileNum > 0 ) {
00628
00629 removeTile(Game->MoveListData(Game->TileNum-1), false);
00630 removeTile(Game->MoveListData(Game->TileNum-1));
00631 drawTileNumber();
00632 animateBackwardsTimer->start();
00633 } else {
00634
00635 stopEndAnimation();
00636 }
00637 }
00638
00639 void BoardWidget::stopEndAnimation()
00640 {
00641 animateForwardTimer->stop();
00642 animateBackwardsTimer->stop();
00643 }
00644
00645
00646 void BoardWidget::calculateNewGame( int gNumber)
00647 {
00648 cancelUserSelectedTiles();
00649 stopMatchAnimation();
00650 stopEndAnimation();
00651 Game->initialiseRemovedTiles();
00652 setStatusText( i18n("Calculating new game...") );
00653
00654
00655 if( !loadBoard())
00656 {
00657 setStatusText( i18n("Error converting board information!") );
00658 return;
00659 }
00660
00661 if (gNumber == -1) {
00662 gameGenerationNum = KRandom::random();
00663 } else {
00664 gameGenerationNum = gNumber;
00665 }
00666
00667 Game->random.setSeed(gameGenerationNum);
00668
00669
00670
00671 Game->generateTilePositions();
00672
00673
00674
00675 Game->generatePositionDepends();
00676
00677
00678 for( short nr=0; nr<64; nr++ )
00679 {
00680 if( Game->generateStartPosition2() )
00681 {
00682 drawBoard(true);
00683 setStatusText( i18n("Ready. Now it is your turn.") );
00684 cheatsUsed=0;
00685 emit gameCalculated();
00686 return;
00687 }
00688 }
00689
00690 drawBoard(true);
00691 setStatusText( i18n("Error generating new game!") );
00692 }
00693
00694
00695
00696
00697
00698
00699 void BoardWidget::hilightTile( POSITION& Pos, bool on, bool doRepaint )
00700 {
00701 TileSprite * atile = 0;
00702
00703 TileCoord coord = TileCoord(Pos.x,Pos.y,Pos.e);
00704
00705 if (spriteMap.contains(coord)) {
00706 atile = spriteMap.value(coord);
00707 }
00708
00709 if (on) {
00710 Game->setHighlightData(Pos.e,Pos.y,Pos.x,1);
00711 if (atile)
00712 atile->setSelected(true);
00713 } else {
00714 Game->setHighlightData(Pos.e,Pos.y,Pos.x,0);
00715 if (atile)
00716 atile->setSelected(false);
00717 }
00718 }
00719
00720
00721
00722
00723 void BoardWidget::drawBoard(bool showTiles)
00724 {
00725 if (gamePaused) showTiles = false;
00726 if (showTiles) {
00727 populateSpriteMap();
00728 drawTileNumber();
00729 } else {
00730
00731 while (!items()->isEmpty())
00732 delete items()->first();
00733
00734
00735 spriteMap.clear();
00736
00737
00738 QPalette palette;
00739 palette.setBrush( backgroundRole(), theBackground.getBackground() );
00740 setPalette( palette );
00741 setAutoFillBackground (true);
00742 }
00743 }
00744
00745
00746 void BoardWidget::putTileInBoard( POSITION& Pos, bool doRepaint )
00747 {
00748 short E=Pos.e;
00749 short Y=Pos.y;
00750 short X=Pos.x;
00751
00752
00753 Game->putTile( E, Y, X, Pos.f );
00754 Game->setHighlightData(E,Y,X,0);
00755
00756 QPixmap s;
00757 QPixmap us;
00758 QPixmap f;
00759 s= theTiles.selectedTile(m_angle);
00760 us= theTiles.unselectedTile(m_angle);
00761 f= theTiles.tileface(Game->BoardData(E,Y,X)-TILE_OFFSET);
00762 TileSprite * thissprite = new TileSprite(this, us, s, f, m_angle, false);
00763 thissprite->show();
00764 spriteMap.insert(TileCoord(X,Y,E), thissprite);
00765
00766 updateSpriteMap();
00767 }
00768
00769
00770
00771
00772 void BoardWidget::removeTile( POSITION& Pos , bool doRepaint)
00773 {
00774 short E = Pos.e;
00775 short Y = Pos.