summaryrefslogtreecommitdiffstats
path: root/klines/linesboard.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'klines/linesboard.cpp')
-rw-r--r--klines/linesboard.cpp754
1 files changed, 754 insertions, 0 deletions
diff --git a/klines/linesboard.cpp b/klines/linesboard.cpp
new file mode 100644
index 00000000..5078b8f9
--- /dev/null
+++ b/klines/linesboard.cpp
@@ -0,0 +1,754 @@
+/***************************************************************************
+ begin : Fri May 19 2000
+ copyright : (C) 2000 by Roman Merzlyakov
+ email : roman@sbrf.barrt.ru
+ copyright : (C) 2000 by Roman Razilov
+ email : Roman.Razilov@gmx.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 <qpainter.h>
+#include <qcolor.h>
+#include <qcursor.h>
+#include <qkeycode.h>
+#include <qtooltip.h>
+#include <stdlib.h>
+
+#include <kapplication.h>
+#include <klocale.h>
+#include <kpixmap.h>
+#include <kpixmapeffect.h>
+
+#include "linesboard.h"
+#include "linesboard.moc"
+
+/*
+ Constructs a LinesBoard widget.
+*/
+
+LinesBoard::LinesBoard( BallPainter * abPainter, QWidget* parent, const char* name )
+ : Field( parent, name )
+{
+ demoLabel = 0;
+ bGameOver = false;
+ anim = ANIM_NO;
+ focusX = -1;
+ focusY = -1;
+// waypos = 0;
+// waylen = 0;
+// jumpingRow = -1;
+// jumpingCol = -1;
+ painting = 0;
+ way = new Waypoints[NUMCELLSW*NUMCELLSH];
+
+ bPainter = abPainter;
+
+ setFocusPolicy( NoFocus );
+ setBackgroundColor( gray );
+
+ setMouseTracking( FALSE );
+ setFixedSize(wHint(), hHint());
+
+ timer = new QTimer(this);
+ connect( timer, SIGNAL(timeout()), SLOT(timerSlot()) );
+ timer->start( TIMERCLOCK, FALSE );
+
+}
+
+/*
+ Destructor: deallocates memory for contents
+*/
+
+LinesBoard::~LinesBoard()
+{
+ timer->stop();
+ delete timer;
+ delete [] way;
+}
+
+void LinesBoard::startDemoMode()
+{
+ level = DEMO_LEVEL;
+ rnd_demo = KRandomSequence(DEMO_SEQUENCE);
+ bAllowMove = false;
+}
+
+void LinesBoard::adjustDemoMode(bool allowMove, bool off)
+{
+ bAllowMove = allowMove;
+ if (off)
+ level = -2;
+}
+
+void LinesBoard::demoAdjust(int a)
+{
+ rnd_demo.modulate(a);
+}
+
+
+void LinesBoard::placeBalls(int pnextBalls[BALLSDROP])
+{
+ for(int i=0; i < BALLSDROP; i++)
+ nextBalls[i] = pnextBalls[i];
+
+ nextBallToPlace = 0;
+ placeBall();
+}
+
+void LinesBoard::placeBall( )
+{
+ char* xx = (char*)malloc( sizeof(char)*NUMCELLSW*NUMCELLSH );
+ char* yy = (char*)malloc( sizeof(char)*NUMCELLSW*NUMCELLSH );
+ int empty = 0;
+ for(int y=0; y<NUMCELLSH; y++)
+ for(int x=0; x<NUMCELLSW; x++)
+ if( getBall(x,y) == NOBALL )
+ {
+ xx[empty] = x;
+ yy[empty] = y;
+ empty++;
+ };
+
+ if (empty)
+ {
+ int color = nextBalls[nextBallToPlace];
+ int pos = 0;
+ if ((level == DEMO_LEVEL) || (level == 0))
+ {
+ pos = random(empty);
+ }
+ else
+ {
+ int best_pos = 0;
+ int best_score = level > 0 ? 1000: -1000;
+ int maxtry = level > 0 ? level+1 : 1-level;
+ for(int i=0;i<maxtry;i++)
+ {
+ int pos = random(empty);
+ int score = calcPosScore(xx[pos], yy[pos], color) - calcPosScore(xx[pos], yy[pos], NOBALL);
+ if (((level > 0) && (score < best_score)) ||
+ ((level < 0) && (score > best_score)))
+ {
+ best_pos = pos;
+ best_score = score;
+ }
+ }
+ pos = best_pos;
+ }
+
+ putBall( xx[pos], yy[pos], color );
+ clearAnim();
+ setAnim( xx[pos], yy[pos], ANIM_BORN );
+ nextBallToPlace++;
+ AnimStart(ANIM_BORN);
+ free(xx);
+ free(yy);
+ }
+ else
+ {
+ free(xx);
+ free(yy);
+ emit endGame();
+ }
+}
+
+
+/*id LinesBoard::doAfterBalls() {
+ erase5Balls();
+ repaint(FALSE);
+}
+*/
+/*
+ Sets the size of the table
+*/
+
+int LinesBoard::width() { return CELLSIZE * NUMCELLSW; }
+int LinesBoard::height() { return CELLSIZE * NUMCELLSH; }
+int LinesBoard::wHint() { return width(); }
+int LinesBoard::hHint() { return height(); }
+
+void LinesBoard::setGameOver(bool b)
+{
+ bGameOver = b;
+ focusX = -1;
+ focusY = -1;
+}
+
+
+void LinesBoard::paintEvent( QPaintEvent* )
+{
+ QPainter *paint;
+ KPixmap *pixmap = 0;
+ if (bGameOver)
+ {
+ pixmap = new KPixmap();
+ pixmap->resize(width(), height());
+ paint = new QPainter( pixmap );
+ }
+ else
+ {
+ paint = new QPainter( this );
+ }
+
+ for( int y=0; y < NUMCELLSH; y++ ){
+ for( int x=0; x < NUMCELLSW; x++ ){
+ if( getBall(x,y) == NOBALL )
+ {
+ paint->drawPixmap(x*CELLSIZE, y*CELLSIZE, bPainter->GetBackgroundPix() );
+ }
+ else
+ {
+ paint->drawPixmap(x*CELLSIZE, y*CELLSIZE,
+ bPainter->GetBall(getBall(x,y),animstep,getAnim(x,y)));
+ }
+ }
+ }
+ if (bGameOver)
+ {
+ paint->end();
+
+ KPixmapEffect::fade(*pixmap, 0.5, Qt::black);
+
+ QPainter p(this);
+ p.drawPixmap(0,0, *pixmap);
+ delete pixmap;
+
+ QFont gameover_font = font();
+ gameover_font.setPointSize(48);
+ gameover_font.setBold(true);
+ p.setFont(gameover_font);
+ p.setPen(Qt::white);
+ QString gameover_text = i18n("Game Over");
+ p.drawText(0, 0, width(), height(), AlignCenter|Qt::WordBreak, gameover_text);
+ }
+ else
+ {
+ if ((focusX >= 0) && (focusX < NUMCELLSW) &&
+ (focusY >= 0) && (focusY < NUMCELLSH))
+ {
+ QRect r;
+ r.setX(focusX*CELLSIZE+2);
+ r.setY(focusY*CELLSIZE+2);
+ r.setWidth(CELLSIZE-4);
+ r.setHeight(CELLSIZE-4);
+ paint->setPen(QPen(Qt::DotLine));
+ paint->drawRect(r);
+ }
+ }
+ delete paint;
+}
+
+/*
+ Handles mouse press events for the LinesBoard widget.
+*/
+void LinesBoard::mousePressEvent( QMouseEvent* e )
+{
+ if (bGameOver) return;
+ if ((level == DEMO_LEVEL) && (!bAllowMove) && e->spontaneous()) return;
+
+ int curRow = e->y() / CELLSIZE;
+ int curCol = e->x() / CELLSIZE;
+
+ placePlayerBall(curCol, curRow);
+}
+
+void LinesBoard::placePlayerBall(int curCol, int curRow)
+{
+ //check range
+ if (!checkBounds( curCol, curRow ) )
+ return;
+// if( running || anim != ANIM_NO ) return;
+ if(anim != ANIM_JUMP && anim != ANIM_NO) return;
+ if ( anim == ANIM_JUMP )
+ {
+ if ( getBall(curCol,curRow) == NOBALL )
+ {
+ if(existPath(jumpingCol, jumpingRow, curCol, curRow))
+ {
+ saveRandomState();
+ rnd.modulate(jumpingCol);
+ rnd.modulate(jumpingRow);
+ rnd.modulate(curCol);
+ rnd.modulate(curRow);
+ saveUndo();
+ AnimStart(ANIM_RUN);
+ }
+ }
+ else
+ AnimJump(curCol,curRow);
+ }
+ else
+ AnimJump(curCol,curRow);
+}
+
+/*
+ Move focus thingy
+*/
+void LinesBoard::moveFocus(int dx, int dy)
+{
+ if (bGameOver) return;
+ if ((level == DEMO_LEVEL) && (!bAllowMove)) return;
+ if (focusX == -1)
+ {
+ focusX = 0;
+ focusY = 0;
+ }
+ else
+ {
+ focusX = (focusX + dx + NUMCELLSW) % NUMCELLSW;
+ focusY = (focusY + dy + NUMCELLSH) % NUMCELLSH;
+ }
+ repaint(FALSE);
+}
+
+void LinesBoard::moveLeft()
+{
+ moveFocus(-1, 0);
+}
+
+void LinesBoard::moveRight()
+{
+ moveFocus(1, 0);
+}
+
+void LinesBoard::moveUp()
+{
+ moveFocus(0, -1);
+}
+
+void LinesBoard::moveDown()
+{
+ moveFocus(0, 1);
+}
+
+void LinesBoard::placePlayerBall()
+{
+ if (bGameOver) return;
+ if ((level == DEMO_LEVEL) && (!bAllowMove)) return;
+ placePlayerBall(focusX, focusY);
+}
+
+void LinesBoard::AnimJump(int x, int y ) {
+ if ( getBall(x,y) != NOBALL )
+ if (!( anim == ANIM_JUMP && jumpingCol == x && jumpingRow == y ))
+ if ( AnimEnd() )
+ {
+ clearAnim();
+ setAnim(x,y,ANIM_JUMP);
+ jumpingCol = x;
+ jumpingRow = y;
+ AnimStart(ANIM_JUMP);
+ }
+}
+void LinesBoard::AnimStart(int panim) {
+ if (anim != ANIM_NO)
+ AnimEnd();
+ animstep = 0;
+ animdelaystart = 1;
+ switch(panim) {
+ case ANIM_NO:
+ break;
+ case ANIM_BORN:
+ animdelaystart=1;
+ animmax = BOOMBALLS;
+ break;
+ case ANIM_JUMP:
+ direction = -1;
+ animstep = 4;
+ animmax = PIXTIME -1;
+ break;
+ case ANIM_RUN:
+ animdelaystart=3;
+ // do first step on next timer;
+ animdelaycount = 0;
+ // animmax already set
+ break;
+ case ANIM_BURN:
+ animdelaystart=1;
+ animmax = FIREBALLS + FIREPIX - 1;
+ break;
+ default:
+ ;
+ }
+ anim = panim;
+ animdelaycount = animdelaystart;
+}
+
+int LinesBoard::AnimEnd( )
+{
+ if (anim == ANIM_NO ) return true;
+ int oldanim = anim;
+ anim = ANIM_NO;
+ if (oldanim == ANIM_RUN) {
+ if (animstep != animmax) {
+ moveBall(way[animstep].x,way[animstep].y,way[animmax].x,way[animmax].y);
+ }
+ if ( erase5Balls() == 0 ) {
+ emit endTurn();
+ return true;
+ }
+ else
+ return false;
+ }
+ else if ( oldanim == ANIM_BURN )
+ {
+ emit eraseLine( deleteAnimatedBalls() );
+ repaint(FALSE);
+ if ( nextBallToPlace < BALLSDROP )
+ {
+ placeBall();
+ // continue with born
+ return false;
+ }
+ else
+ {
+ emit userTurn();
+ return true;
+ }
+ }
+ else if ( oldanim == ANIM_BORN )
+ {
+ if ( erase5Balls() == 0 )
+ {
+ if ( freeSpace() > 0)
+ {
+ if ( nextBallToPlace < BALLSDROP )
+ {
+ placeBall();
+ return false;
+ }
+ else
+ {
+ emit userTurn();
+ return true;
+ }
+ }
+ else
+ {
+ emit endGame();
+ return false;
+ }
+ }
+ else
+ {
+ // wait for user input
+ emit userTurn();
+ return true;
+ }
+ }
+ emit userTurn();
+ return true;
+}
+
+void LinesBoard::AnimNext() {
+ if ( anim != ANIM_NO )
+ {
+ if ( anim == ANIM_JUMP ) {
+ if ( (direction > 0 && animstep == animmax) || ( direction < 0 && animstep == 0))
+ direction = -direction;
+ animstep += direction;
+ repaint(FALSE);
+ } else {
+ if ( animstep >= animmax )
+ AnimEnd();
+ else {
+ animdelaycount--;
+ if (animdelaycount <= 0) {
+ if ( anim == ANIM_RUN )
+ moveBall(way[animstep].x,way[animstep].y,way[animstep+1].x,way[animstep+1].y);
+ animstep++;
+ animdelaycount = animdelaystart;
+ repaint( FALSE );
+ }
+ }
+ }
+ }
+}
+int LinesBoard::getAnim( int x, int y )
+{
+ return (( Field::getAnim(x,y) != ANIM_NO )? anim : ANIM_NO);
+}
+
+void LinesBoard::timerSlot()
+{
+ AnimNext();
+}
+
+int LinesBoard::erase5Balls()
+{
+ int nb=5; // minimum balls for erasure
+
+ bool bit_erase[NUMCELLSH][NUMCELLSW]; //bool array for balls, that must be erased
+ for(int y=0; y<NUMCELLSH; y++)
+ for(int x=0; x<NUMCELLSW; x++)
+ bit_erase[y][x] = false;
+
+ int color,newcolor;
+ int count;
+ //for horisontal
+
+ for(int y=0; y<NUMCELLSH; y++) {
+ count = 1;
+ color = NOBALL;
+ for(int x=0; x<NUMCELLSW; x++){
+ if ( (newcolor = getBall(x,y)) == color) {
+ if ( color != NOBALL) {
+ count++;
+ if ( count >= nb )
+ if ( count == nb )
+ for (int i = 0; i < nb; i++)
+ bit_erase[y][x-i] = true;
+ else bit_erase[y][x] = true;
+ }
+ } else {
+ color = newcolor;
+ count = 1;
+ }
+ }
+ }
+
+ //for vertical
+ for(int x=0; x<NUMCELLSW; x++) {
+ count = 0;
+ color = NOBALL;
+ for(int y=0; y<NUMCELLSH; y++){
+ if ( (newcolor = getBall(x,y)) == color) {
+ if ( color != NOBALL) {
+ count++;
+ if ( count >= nb )
+ if ( count == nb )
+ for (int i = 0; i < nb; i++)
+ bit_erase[y-i][x] = true;
+ else bit_erase[y][x] = true;
+ }
+ } else {
+ color = newcolor;
+ count = 1;
+ }
+ }
+ }
+
+
+ //for left-down to rigth-up diagonal
+ for ( int xs = NUMCELLSW-1,ys = NUMCELLSH-nb; xs >= nb-1; ) {
+ count = 0;
+ color = NOBALL;
+ for ( int x = xs, y = ys; x >= 0 && y < NUMCELLSH; x--, y++ ) {
+ if ( (newcolor = getBall(x,y)) == color) {
+ if ( color != NOBALL) {
+ count++;
+ if ( count >= nb )
+ if ( count == nb )
+ for (int i = 0; i < nb; i++)
+ bit_erase[y-i][x+i] = true;
+ else bit_erase[y][x] = true;
+ }
+ } else {
+ color = newcolor;
+ count = 1;
+ }
+ }
+ if ( ys > 0 ) ys--; else xs--;
+ }
+
+
+ //for left-up to rigth-down diagonal
+ for ( int xs = 0,ys = NUMCELLSH-nb; xs <= NUMCELLSW-nb; )
+ {
+ count = 0;
+ color = NOBALL;
+ for ( int x = xs, y = ys; x < NUMCELLSW && y < NUMCELLSH; x++, y++ )
+ {
+ if ( (newcolor = getBall(x,y)) == color)
+ {
+ if ( color != NOBALL)
+ {
+ count++;
+ if ( count >= nb )
+ if ( count == nb )
+ for (int i = 0; i < nb; i++)
+ bit_erase[y-i][x-i] = true;
+ else
+ bit_erase[y][x] = true;
+ }
+ }
+ else
+ {
+ color = newcolor;
+ count = 1;
+ }
+ }
+ if ( ys > 0 ) ys--; else xs++;
+ }
+
+ //remove all lines balls, that more than nb
+ int cb=0;
+ for(int y=0; y < NUMCELLSH; y++)
+ for(int x=0; x < NUMCELLSW; x++)
+ {
+ if (bit_erase[y][x])
+ {
+ setAnim(x,y,ANIM_YES);
+ cb++;
+ }
+ else
+ {
+ setAnim(x,y,ANIM_NO);
+ }
+ }
+ if ( cb > 0 )
+ {
+ AnimStart(ANIM_BURN);
+ //emit eraseLine(cb);
+ }
+
+ return cb;
+}
+
+
+#define GO_EMPTY 4
+#define GO_A 5
+#define GO_B 6
+#define GO_BALL 7
+
+bool LinesBoard::existPath(int bx, int by, int ax, int ay)
+{
+ int dx[4]={1,-1, 0, 0};
+ int dy[4]={0, 0, 1,-1};
+
+ if (getBall(ax,ay) != NOBALL)
+ return false;
+
+ char pf[NUMCELLSH][NUMCELLSW];
+ for(int y=0; y<NUMCELLSH; y++)
+ for(int x=0; x<NUMCELLSW; x++)
+ pf[y][x] = (getBall(x,y) == NOBALL) ? GO_EMPTY:GO_BALL;
+
+ Waypoints lastchanged[2][NUMCELLSH*NUMCELLSW];
+
+ int lastChangedCount[2];
+ int currentchanged = 0;
+ int nextchanged = 1;
+ lastchanged[currentchanged][0].x = ax;
+ lastchanged[currentchanged][0].y = ay;
+ lastChangedCount[currentchanged] = 1;
+ pf[ay][ax]=GO_A;
+ pf[by][bx]=GO_B;
+
+ // int expanded;
+ bool B_reached = false;
+
+ do
+ {
+ lastChangedCount[nextchanged] = 0;
+ for(int dir=0; dir<4; dir++)
+ {
+ for ( int i = 0 ; i < lastChangedCount[currentchanged]; i++ )
+ {
+ int nx = lastchanged[currentchanged][i].x + dx[dir];
+ int ny = lastchanged[currentchanged][i].y + dy[dir];
+ if( (nx>=0) && (nx<NUMCELLSW) && (ny>=0) && (ny<NUMCELLSH) )
+ {
+ if( pf[ny][nx]==GO_EMPTY ||( nx==bx && ny==by ))
+ {
+ pf[ny][nx] = dir;
+ lastchanged[nextchanged][lastChangedCount[nextchanged]].x = nx;
+ lastchanged[nextchanged][lastChangedCount[nextchanged]].y = ny;
+ lastChangedCount[nextchanged]++;
+ };
+ if( (nx==bx) && (ny==by) )
+ {
+ B_reached = true;
+ break;
+ }
+ }
+ }
+ }
+ nextchanged = nextchanged ^ 1;
+ currentchanged = nextchanged ^ 1;
+ // currentchanged,lastChangedCount[currentchanged]);
+ } while(!B_reached && lastChangedCount[currentchanged] != 0);
+
+ if (B_reached) {
+ AnimStart( ANIM_BLOCK);
+ animmax = 0;
+// waypos = 0;
+ int x, y,dir;
+ for( x = bx, y = by; (dir = pf[y][x]) != GO_A;x -=dx[dir],y -= dy[dir]) {
+ way[animmax].x = x;
+ way[animmax].y = y;
+ animmax++;
+ }
+ way[animmax].x = x;
+ way[animmax].y = y;
+ }
+ return B_reached;
+}
+
+void LinesBoard::undo()
+{
+ AnimEnd();
+ restoreUndo();
+ restoreRandomState();
+ repaint( FALSE );
+}
+
+void LinesBoard::showDemoText(const QString &text)
+{
+ if (!demoLabel)
+ {
+ demoLabel = new QLabel(0, "demoTip", WStyle_StaysOnTop | WStyle_Customize | WStyle_NoBorder | WStyle_Tool | WX11BypassWM );
+ demoLabel->setMargin(1);
+ demoLabel->setIndent(0);
+ demoLabel->setAutoMask( FALSE );
+ demoLabel->setFrameStyle( QFrame::Plain | QFrame::Box );
+ demoLabel->setLineWidth( 1 );
+ demoLabel->setAlignment( AlignHCenter | AlignTop );
+ demoLabel->setPalette(QToolTip::palette());
+ demoLabel->polish();
+ }
+ demoLabel->setText(text);
+ demoLabel->adjustSize();
+ QSize s = demoLabel->sizeHint();
+ QPoint p = QPoint(x() + (width()-s.width())/2, y() + (height()-s.height())/2);
+ demoLabel->move(mapToGlobal(p));
+ demoLabel->show();
+}
+
+void LinesBoard::hideDemoText()
+{
+ if (demoLabel)
+ demoLabel->hide();
+}
+
+void LinesBoard::demoClick(int x, int y)
+{
+ QPoint lDest = QPoint(x*CELLSIZE+(CELLSIZE/2), y*CELLSIZE+(CELLSIZE/2));
+ QPoint dest = mapToGlobal(lDest);
+ QPoint cur = QCursor::pos();
+ for(int i = 0; i < 25;)
+ {
+ i++;
+ QPoint p = cur + i*(dest-cur) / 25;
+ QCursor::setPos(p);
+ QApplication::flushX();
+ QTimer::singleShot(80, this, SLOT(demoClickStep()));
+ kapp->enter_loop();
+ }
+ QCursor::setPos(dest);
+ QMouseEvent ev(QEvent::MouseButtonPress, lDest, dest, LeftButton, LeftButton);
+ mousePressEvent(&ev);
+}
+
+void LinesBoard::demoClickStep()
+{
+ kapp->exit_loop();
+}