You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
knights/knights/board_2d.cpp

808 lines
20 KiB

/***************************************************************************
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 <kiconeffect.h>
#include <tqpainter.h>
#include <tqtimer.h>
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, TQT_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( TQT_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 )
{
register char tmp(0);
if( init )
return;
/* Set Qt::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, TQT_TQPAINTDEVICE(&cache->BorderLightOn), cache->BorderLightOn.rect() );
myBlit( white, TQT_TQPAINTDEVICE(&cache->BorderLightOff), cache->BorderLightOff.rect() );
}
else
{
myBlit( black, TQT_TQPAINTDEVICE(&cache->BorderLightOff), cache->BorderLightOff.rect() );
myBlit( white, TQT_TQPAINTDEVICE(&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 ), TQT_TQPAINTDEVICE(&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( TQT_TQPAINTDEVICE(&buffer), 0, 0, TQT_TQPAINTDEVICE(&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( TQT_TQPAINTDEVICE(&buffer), 0, 0, TQT_TQPAINTDEVICE(&cache->HighlightMove), 0, 0, -1, -1, TQt::CopyROP, FALSE);
break;
case NOTE_ATTACK: // Fall Through
case NOTE_ENPASSANT:
bitBlt( TQT_TQPAINTDEVICE(&buffer), 0, 0, TQT_TQPAINTDEVICE(&cache->HighlightAttack), 0, 0, -1, -1, TQt::CopyROP, FALSE);
break;
default:
break;
}
if( !isSprite(pos) )
{
TQPixmap chessman = getChessman( pos );
bitBlt( TQT_TQPAINTDEVICE(&buffer), 0, 0, TQT_TQPAINTDEVICE(&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 ), TQT_TQPAINTDEVICE(&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,
TQT_TQPAINTDEVICE(&DragSprite->PIXMAP_FlipFrame),
DragSprite->PIXMAP_FlipFrame.rect() );
sprites.removeRef( DragSprite );
DragSprite = NULL;
drawPosition( tmp );
commit();
lastMoveWasDrag = TRUE;
}
if(event->button() == Qt::LeftButton)
{
emit leftClick( position( event->pos() ) );
}
if(event->button() == Qt::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() & Qt::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() & Qt::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;
register 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,
TQT_TQPAINTDEVICE(&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,
TQT_TQPAINTDEVICE(&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, TQT_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();
}