/*************************************************************************** board_2d.cpp - description ------------------- begin : Fri Feb 28 2003 copyright : (C) 2003 by The Knights Project email : knights-general@lists.sourceforge.net ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "board_2d.moc" #include "resource.h" #include "logic.h" #include "knightspixcache.h" #include #include #include static int MAX_STEPS = 18; board_2d::board_2d(TQWidget *parent, const char *name, resource *Rsrc, logic *Lgc ) : board_base(parent,name,Rsrc,Lgc) { updateX1 = updateY1 = 4000; updateX2 = updateY2 = -4000; init = TRUE; premoveFrom = Null; premoveTo = Null; DragSprite = NULL; lastMoveWasDrag = FALSE; cache = myResource->pixCache; int size = 8 + ( 1 * ( myResource->ThemeBorder == TRUE ) ); sprites.setAutoDelete( TRUE ); /* Setup Pixmaps */ myself.setOptimization( TQPixmap::BestOptim ); myself.resize( myResource->ThemeSize * size, myResource->ThemeSize * size); myself.fill(); /* Setup self */ setBackgroundMode( TQt::NoBackground ); show(); } board_2d::~board_2d() { } /////////////////////////////////////// // // board_2d::coords // /////////////////////////////////////// TQPoint board_2d::coords( const int &rank, const int &file ) { TQPoint tmp; if( orientation == 0 ) { tmp.setX( myResource->ThemeSize * file ); tmp.setY( myResource->ThemeSize * ( 7 - rank ) ); } else { tmp.setX( myResource->ThemeSize * ( 7 - file ) ); tmp.setY( myResource->ThemeSize * rank ); } if( myResource->ThemeBorder ) { tmp.setX( tmp.x() + ( myResource->ThemeSize >> 1 ) ); tmp.setY( tmp.y() + ( myResource->ThemeSize >> 1 ) ); } return tmp; } /////////////////////////////////////// // // board_2d::position // /////////////////////////////////////// int board_2d::position( const TQPoint &_point ) { int file, rank; int themeSize = myResource->ThemeSize; TQPoint point( _point ); if( myResource->ThemeBorder ) { point.setX( point.x() - ( themeSize >> 1 ) ); point.setY( point.y() - ( themeSize >> 1 ) ); } if( !orientation ) { file = point.x() / themeSize; rank = 7 - ( point.y() / themeSize ); } else { file = 7 - ( point.x() / themeSize ); rank = point.y() / themeSize; } return ( ( rank << 3 ) + file ); } /////////////////////////////////////// // // board_2d::drawMove // /////////////////////////////////////// void board_2d::drawMove( const ChessMove &chessMove, const bool &reverse ) { char fromPtr, toPtr, takenPtr(Null); if( reverse ) { fromPtr = ( chessMove.toRank << 3 ) + chessMove.toFile; toPtr = ( chessMove.fromRank << 3 ) + chessMove.fromFile; } else { fromPtr = ( chessMove.fromRank << 3 ) + chessMove.fromFile; toPtr = ( chessMove.toRank << 3 ) + chessMove.toFile; } /* Position where Man was taken != Target Position; ie. en passant */ if( chessMove.ManTaken != Null ) { takenPtr = myLogic->Pointer( myLogic->chessman[ chessMove.ManTaken ].File, myLogic->chessman[ chessMove.ManTaken ].Rank ); } /* Show Highlights */ if( myResource->OPTION_Show_Last_Move ) { myLogic->current[ fromPtr ].Note = NOTE_MOVE; if( chessMove.ManTaken != Null ) { myLogic->current[ toPtr ].Note = NOTE_ATTACK; } else { myLogic->current[ toPtr ].Note = NOTE_MOVE; } } /* Show Animation */ if( myResource->OPTION_Animate_Moves && !lastMoveWasDrag && isVisible() ) { TQTimer::singleShot( 0, this, TQ_SLOT( updateSprites() ) ); sprite *spritePtr = new sprite; spritePtr->Steps = 1; spritePtr->Restore = FALSE; spritePtr->POSITION_Origin = fromPtr; spritePtr->POSITION_Destination = toPtr; spritePtr->POSITION_TargetTaken = takenPtr; if( !reverse ) { spritePtr->POINT_Origin = coords( chessMove.fromRank, chessMove.fromFile ); spritePtr->POINT_Destination = coords( chessMove.toRank, chessMove.toFile ); spritePtr->PIXMAP_Sprite = getChessman( spritePtr->POSITION_Destination ); } else { spritePtr->POINT_Origin = coords( chessMove.toRank, chessMove.toFile ); spritePtr->POINT_Destination = coords( chessMove.fromRank, chessMove.fromFile ); spritePtr->PIXMAP_Sprite = getChessman( spritePtr->POSITION_Origin ); } spritePtr->POINT_Current = spritePtr->POINT_Origin; spritePtr->PIXMAP_FlipFrame.resize( spritePtr->PIXMAP_Sprite.size() ); sprites.append( spritePtr ); } else { /* Draw this position only if we're not animating */ drawPosition( toPtr ); if( takenPtr != Null ) { drawPosition( takenPtr ); } } /* Draw the originating position */ drawPosition( fromPtr ); if( TQString( chessMove.SAN ).contains( "o-o", FALSE ) ) { /* This is a castle */ ChessMove newMove; strcpy( newMove.SAN, TQString( "no" ).latin1() ); newMove.fromRank = chessMove.fromRank; newMove.toRank = chessMove.toRank; newMove.ManTaken = Null; if( TQString( chessMove.SAN ).contains( "o-o-o", FALSE ) ) { /* Queenside */ newMove.fromFile = 0; newMove.toFile = 3; } else { /* Kingside */ newMove.fromFile = 7; newMove.toFile = 5; } drawMove( newMove, reverse ); } lastMoveWasDrag = FALSE; } /////////////////////////////////////// // // board_2d::resizeBoard( TQ_SLOT ) // /////////////////////////////////////// void board_2d::resizeBoard( void ) { int size = 8 + ( 1 * ( myResource->ThemeBorder == TRUE ) ); init = FALSE; /* Resize myself */ myself.resize( myResource->ThemeSize * size, myResource->ThemeSize * size); myself.fill(); /* Finish up */ setFixedSize( myResource->ThemeSize * size, myResource->ThemeSize * size); redrawAll(); int inverseSize = IMAGE_MAX - myResource->ThemeSize; MAX_STEPS = ( ( inverseSize * inverseSize ) / IMAGE_MAX ) + 7; } /////////////////////////////////////// // // board_2d::redrawAll // /////////////////////////////////////// void board_2d::redrawAll( void ) { char tmp(0); if( init ) return; /* Set Orientation */ orientation = myResource->OPTION_Board_Orientation; if( localArmy != WHITE ) orientation = !orientation; if( flip ) orientation = !orientation; /* Set Border */ if( myResource->ThemeBorder ) { if( orientation ) { TQWMatrix matrix; matrix.rotate( 180.0 ); myself = cache->Border.xForm( matrix ); } else myself = cache->Border; } /* Redraw All Positions */ while( tmp < 64 ) drawPosition( tmp++ ); redrawLights(); /* Make sure everything is repainted */ updateX1 = updateY1 = 0; updateX2 = updateY2 = IMAGE_MAX * 9; commit(); } /////////////////////////////////////// // // board_2d::redrawLights // /////////////////////////////////////// void board_2d::redrawLights( void ) { int half, four; TQPoint black, white; if( myResource->ThemeBorder ) { half = myResource->ThemeSize >> 1; four = myResource->ThemeSize << 2; if( !orientation ) { black.setX( 0 ); black.setY( four ); white.setX( 0 ); white.setY( four + half ); } else { black.setX( myself.width() - half ); black.setY( four + half ); white.setX( myself.width() - half ); white.setY( four ); } if( myLogic->OnMove != WHITE ) { myBlit( black, &cache->BorderLightOn, cache->BorderLightOn.rect() ); myBlit( white, &cache->BorderLightOff, cache->BorderLightOff.rect() ); } else { myBlit( black, &cache->BorderLightOff, cache->BorderLightOff.rect() ); myBlit( white, &cache->BorderLightOn, cache->BorderLightOn.rect() ); } } } /////////////////////////////////////// // // board_2d::drawPosition // /////////////////////////////////////// void board_2d::drawPosition( const int &pos ) { int rank = pos >> 3; int file = pos % 8; TQPixmap buffer; TQString cacheName = TQString::number( myResource->ThemeSize ); TQImage tempImage; if( ( pos < 0 ) || ( pos > 63 ) ) return; /* Build the cache ref name */ if( color( rank, file ) ) cacheName += "L"; else cacheName += "D"; if( !paused ) { switch( myLogic->current[pos].Note ) { case NOTE_SELECT: case NOTE_HIGHLIGHT: cacheName += "S"; break; case NOTE_MOVE: case NOTE_CASTLE: case NOTE_PAWN_DOUBLE: cacheName += "M"; break; case NOTE_ATTACK: case NOTE_ENPASSANT: cacheName += "A"; break; default: cacheName += " "; } if( ( myLogic->current[pos].ManPtr != Null ) && ( !isSprite( pos ) ) ) { if( myLogic->chessman[ myLogic->current[pos].ManPtr ].Army == WHITE ) cacheName += "W"; else cacheName += "B"; switch( myLogic->chessman[myLogic->current[pos].ManPtr].Type ) { case King: cacheName += "K"; break; case Queen: cacheName += "Q"; break; case Bishop: cacheName += "B"; break; case Knight: cacheName += "N"; break; case Rook: cacheName += "R"; break; case Pawn: cacheName += "P"; break; default: break; } if( ( pos == premoveFrom ) || ( pos == premoveTo ) ) cacheName += "t"; // The means it's transparent } } else cacheName += " "; if( cache->find( cacheName, buffer ) ) { /* Cache Hit... no need to redraw */ if( myResource->OPTION_Show_Coord ) drawCoords( &buffer, pos ); myBlit( coords( rank, file ), &buffer, buffer.rect() ); return; } /* Cache miss Draw the pixmap */ if( color( rank, file ) ) buffer = cache->SquareLight; else buffer = cache->SquareDark; switch( myLogic->current[pos].Note ) { case NOTE_HIGHLIGHT: // Fall Through case NOTE_SELECT: bitBlt( &buffer, 0, 0, &cache->HighlightSelect, 0, 0, -1, -1, TQt::CopyROP, FALSE); break; case NOTE_MOVE: // Fall Through case NOTE_CASTLE: // Fall Through case NOTE_PAWN_DOUBLE: bitBlt( &buffer, 0, 0, &cache->HighlightMove, 0, 0, -1, -1, TQt::CopyROP, FALSE); break; case NOTE_ATTACK: // Fall Through case NOTE_ENPASSANT: bitBlt( &buffer, 0, 0, &cache->HighlightAttack, 0, 0, -1, -1, TQt::CopyROP, FALSE); break; default: break; } if( !isSprite(pos) ) { TQPixmap chessman = getChessman( pos ); bitBlt( &buffer, 0, 0, &chessman, 0, 0, -1, -1, TQt::CopyROP, FALSE); } /* Now add this pixmap to the cache */ cache->add( cacheName, buffer ); /* */ if( myResource->OPTION_Show_Coord ) drawCoords( &buffer, pos ); myBlit( coords( rank, file ), &buffer, buffer.rect() ); } /////////////////////////////////////// // // board_2d::drawCoords // /////////////////////////////////////// void board_2d::drawCoords( TQPixmap *pic, const int &pos ) { TQPainter painter; TQString letter; int themeSize = myResource->ThemeSize - 4; int targetRank, targetFile; if( orientation == 0 ) { targetRank = 0; targetFile = 0; } else { targetRank = 7; targetFile = 7; } /* Draw Rank */ if( ( pos >> 3 ) == targetRank ) { letter = TQString( TQString("abcdefgh").at( pos % 8 ) ); painter.begin( pic ); painter.setFont( myResource->FONT_Standard ); painter.setPen( myResource->COLOR_Notation_Shadow ); painter.drawText( 3, 3, themeSize, themeSize, TQt::AlignRight | TQt::AlignBottom, letter ); painter.setPen( myResource->COLOR_Notation ); painter.drawText( 2, 2, themeSize, themeSize, TQt::AlignRight | TQt::AlignBottom, letter ); painter.end(); } /* Draw File */ if( ( pos % 8 ) == targetFile ) { letter = TQString( TQString("12345678").at( pos >> 3 ) ); painter.begin( pic ); painter.setFont( myResource->FONT_Standard ); painter.setPen( myResource->COLOR_Black ); painter.drawText( 3, 3, themeSize, themeSize, TQt::AlignLeft | TQt::AlignTop, letter ); painter.setPen( myResource->COLOR_White ); painter.drawText( 2, 2, themeSize, themeSize, TQt::AlignLeft | TQt::AlignTop, letter ); painter.end(); } } /////////////////////////////////////// // // board_2d::mouseReleaseEvent // /////////////////////////////////////// void board_2d::mouseReleaseEvent( TQMouseEvent *event ) { event->accept(); if( DragSprite != NULL ) { /* Destroy any sprites being dragged */ int tmp = DragSprite->POSITION_Origin; myBlit( DragSprite->POINT_LastUpdate, &DragSprite->PIXMAP_FlipFrame, DragSprite->PIXMAP_FlipFrame.rect() ); sprites.removeRef( DragSprite ); DragSprite = NULL; drawPosition( tmp ); commit(); lastMoveWasDrag = TRUE; } if(event->button() == TQt::LeftButton) { emit leftClick( position( event->pos() ) ); } if(event->button() == TQt::RightButton) { emit rightClick( position( event->pos() ) ); } } /////////////////////////////////////// // // board_2d::mousePressEvent // /////////////////////////////////////// void board_2d::mousePressEvent( TQMouseEvent *event ) { pressPoint = event->pos(); event->accept(); } /////////////////////////////////////// // // board_2d::mouseMoveEvent // /////////////////////////////////////// void board_2d::mouseMoveEvent( TQMouseEvent *event ) { event->accept(); if( DragSprite == NULL ) { if( event->state() & TQt::LeftButton ) { if( abs( pressPoint.x() - event->pos().x() ) + abs( pressPoint.y() - event->pos().y() ) > 6 ) { /* Begin Dragging a piece */ DragSprite = new sprite; sprites.append( DragSprite ); DragSprite->POINT_Origin = pressPoint; DragSprite->POSITION_Origin = position( pressPoint ); emit leftClick( DragSprite->POSITION_Origin ); // Tell match that we just clicked on this piece if( myLogic->current[ DragSprite->POSITION_Origin ].Note != NOTE_SELECT ) { /* The selection didn't take.. back out. */ sprites.removeRef( DragSprite ); DragSprite = NULL; drawPosition( position( pressPoint ) ); commit(); return; } /* Get the piece image and store it in dragPix */ DragSprite->PIXMAP_Sprite = getChessman( DragSprite->POSITION_Origin ); DragSprite->PIXMAP_FlipFrame.resize( DragSprite->PIXMAP_Sprite.size() ); DragSprite->Restore = FALSE; } else /* Not enough dragging */ return; } // End ( event->state() & TQt::LeftButton ) else return; /* No dragging. Most events should end up here */ } int halfSize = myResource->ThemeSize >> 1; DragSprite->POINT_Current.setX( event->x() - halfSize ); DragSprite->POINT_Current.setY( event->y() - halfSize ); commit(); } /////////////////////////////////////// // // board_2d::getChessman // /////////////////////////////////////// TQPixmap board_2d::getChessman( const int &pos ) { TQPixmap tmp; char type, army; if( pos == premoveTo ) { type = myLogic->chessman[myLogic->current[premoveFrom].ManPtr].Type; army = myLogic->chessman[myLogic->current[premoveFrom].ManPtr].Army; } else { type = myLogic->chessman[myLogic->current[pos].ManPtr].Type; army = myLogic->chessman[myLogic->current[pos].ManPtr].Army; } switch( type ) { case King: if( army == WHITE ) tmp = cache->WhiteKing; else tmp = cache->BlackKing; break; case Queen: if( army == WHITE ) tmp = cache->WhiteQueen; else tmp = cache->BlackQueen; break; case Bishop: if( army == WHITE ) tmp = cache->WhiteBishop; else tmp = cache->BlackBishop; break; case Knight: if( army == WHITE ) tmp = cache->WhiteKnight; else tmp = cache->BlackKnight; break; case Rook: if( army == WHITE ) tmp = cache->WhiteRook; else tmp = cache->BlackRook; break; case Pawn: if( army == WHITE ) tmp = cache->WhitePawn; else tmp = cache->BlackPawn; break; default: break; } if( ( pos == premoveFrom ) || ( pos == premoveTo ) ) { TDEIconEffect::semiTransparent( tmp ); } return tmp; } /////////////////////////////////////// // // board_2d::setPremovePositions // /////////////////////////////////////// void board_2d::setPremovePositions( const int &posF, const int &posT ) { premoveFrom = posF; premoveTo = posT; if( ( posF != Null ) && ( posT != Null ) ) { myLogic->current[posF].Note = NOTE_NONE; myLogic->current[posT].Note = NOTE_NONE; drawPosition( posF ); drawPosition( posT ); commit(); } } /////////////////////////////////////// // // board_2d::commit // /////////////////////////////////////// void board_2d::commit( void ) { drawSprites(); bitBlt( this, updateX1, updateY1, &myself, updateX1, updateY1, updateX2, updateY2, TQt::CopyROP ); updateX1 = updateY1 = 4000; updateX2 = updateY2 = -4000; } /////////////////////////////////////// // // board_2d::paintEvent // /////////////////////////////////////// void board_2d::paintEvent( TQPaintEvent *event ) { /* Paint the Widget */ bitBlt( this, event->rect().topLeft(), &myself, event->rect(), TQt::CopyROP ); } /////////////////////////////////////// // // board_2d::drawSprites // /////////////////////////////////////// void board_2d::drawSprites( void ) { int index; sprite *spritePtr; /* Remove all sprites from the pixmap */ for( index = (signed int)sprites.count() - 1; index > -1; index-- ) { spritePtr = sprites.at(index); if( spritePtr->Restore == FALSE ) { spritePtr->Restore = TRUE; } else { myBlit( spritePtr->POINT_LastUpdate, &spritePtr->PIXMAP_FlipFrame, spritePtr->PIXMAP_FlipFrame.rect() ); } } /* Redraw all sprites */ for( index = 0; index < (signed int)sprites.count(); index++ ) { spritePtr = sprites.at(index); if( ( spritePtr == DragSprite ) || ( spritePtr->Steps < MAX_STEPS ) ) { /* Redraw Sprite */ bitBlt( &spritePtr->PIXMAP_FlipFrame, 0, 0, &myself, spritePtr->POINT_Current.x(), spritePtr->POINT_Current.y(), spritePtr->PIXMAP_FlipFrame.width(), spritePtr->PIXMAP_FlipFrame.height(), TQt::CopyROP ); myBlit( spritePtr->POINT_Current, &spritePtr->PIXMAP_Sprite, spritePtr->PIXMAP_Sprite.rect() ); spritePtr->POINT_LastUpdate = spritePtr->POINT_Current; } else { /* Animation finished */ int origin = spritePtr->POSITION_Origin; int destination = spritePtr->POSITION_Destination; int target = spritePtr->POSITION_TargetTaken; sprites.removeRef( spritePtr ); drawPosition( origin ); drawPosition( destination ); drawPosition( target ); index = -1; } } } /////////////////////////////////////// // // board_2d::updateSprites // /////////////////////////////////////// void board_2d::updateSprites( void ) { if( myResource->OPTION_Animate_Moves ) { for( int index = 0; index < (signed int)sprites.count(); index++ ) { sprite *spritePtr = sprites.at( index ); if( spritePtr == DragSprite ) continue; TQTimer::singleShot( 0, this, TQ_SLOT( updateSprites() ) ); if( spritePtr->Steps < MAX_STEPS ) { double factor = ( 1.0 / (double)MAX_STEPS ) * (double)spritePtr->Steps; int newX = (int)( ( spritePtr->POINT_Destination.x() - spritePtr->POINT_Origin.x() ) * factor ); int newY = (int)( ( spritePtr->POINT_Destination.y() - spritePtr->POINT_Origin.y() ) * factor ); spritePtr->POINT_Current = spritePtr->POINT_Origin + TQPoint( newX, newY ); spritePtr->Steps += sprites.count(); } } commit(); } } /////////////////////////////////////// // // board_2d::isSprite // /////////////////////////////////////// bool board_2d::isSprite( const int &pos ) { for( unsigned int index = 0; index < sprites.count(); index++ ) { sprite *tmpSprite = sprites.at( index ); if( tmpSprite->POSITION_Origin == pos ) { return TRUE; } } return FALSE; } /////////////////////////////////////// // // board_2d::myBlit // /////////////////////////////////////// void board_2d::myBlit( const TQPoint &dp, const TQPaintDevice *src, const TQRect &sr ) { bitBlt( &myself, dp.x(), dp.y(), src, sr.x(), sr.y(), sr.width(), sr.height(), TQt::CopyROP ); if( dp.x() < updateX1 ) updateX1 = dp.x(); if( dp.y() < updateY1 ) updateY1 = dp.y(); if( dp.x() + sr.width() > updateX2 ) updateX2 = dp.x() + sr.width(); if( dp.y() + sr.height() > updateX2 ) updateX2 = dp.y() + sr.height(); }