summaryrefslogtreecommitdiffstats
path: root/libksirtet/base/board.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libksirtet/base/board.cpp')
-rw-r--r--libksirtet/base/board.cpp429
1 files changed, 429 insertions, 0 deletions
diff --git a/libksirtet/base/board.cpp b/libksirtet/base/board.cpp
new file mode 100644
index 00000000..257e72c3
--- /dev/null
+++ b/libksirtet/base/board.cpp
@@ -0,0 +1,429 @@
+#include "board.h"
+#include "board.moc"
+
+#include <knotifyclient.h>
+#include <klocale.h>
+#include <kzoommainwindow.h>
+
+#include "piece.h"
+#include "factory.h"
+#include "baseprefs.h"
+
+using namespace KGrid2D;
+
+//-----------------------------------------------------------------------------
+FixedCanvasView::FixedCanvasView(QWidget *parent, const char *name)
+ : QCanvasView(parent, name, WNoAutoErase)
+{}
+
+QSize FixedCanvasView::sizeHint() const
+{
+ if ( canvas()==0 ) return QSize();
+ return canvas()->size() + 2 * QSize(frameWidth(), frameWidth());
+}
+
+void FixedCanvasView::adjustSize()
+{
+ setFixedSize(sizeHint());
+}
+
+//-----------------------------------------------------------------------------
+const BaseBoard::DirectionData BaseBoard::DIRECTION_DATA[Nb_Direction] = {
+ { SquareBase::Left, Left },
+ { SquareBase::Right, Right },
+ { SquareBase::Down, Up },
+ { SquareBase::Up, Down }
+};
+
+BaseBoard::BaseBoard(bool graphic, QWidget *parent)
+: FixedCanvasView(parent, "board"),
+ GenericTetris(bfactory->bbi.width, bfactory->bbi.height,
+ bfactory->bbi.withPieces, graphic),
+ state(GameOver), timer(this), sequences(0), main(0), _next(0),
+ _arcade(false)
+{
+ if (graphic) {
+ setVScrollBarMode(AlwaysOff);
+ setHScrollBarMode(AlwaysOff);
+ setFrameStyle( QFrame::Panel | QFrame::Sunken );
+
+ sequences = new SequenceArray;
+ main = new BlockInfo(*sequences);
+ setCanvas(main);
+ if (bfactory->bbi.withPieces)
+ _next = new BlockInfo(*sequences);
+ setBlockInfo(main, _next);
+
+ connect(&timer, SIGNAL(timeout()), SLOT(timeout()));
+
+ Piece::info().loadColors();
+ KZoomMainWindow::addWidget(this);
+ }
+}
+
+void BaseBoard::copy(const GenericTetris &g)
+{
+ GenericTetris::copy(g);
+ state = static_cast<const BaseBoard &>(g).state;
+}
+
+void BaseBoard::settingsChanged()
+{
+ Q_ASSERT( graphic() );
+ Piece::info().loadColors();
+}
+
+void BaseBoard::adjustSize()
+{
+ int size = BasePrefs::blockSize();
+
+ sequences->setBlockSize(size);
+ main->resize(matrix().width() * size, matrix().height() * size);
+ for (uint i=0; i<matrix().width(); i++)
+ for (uint j=0; j<firstClearLine(); j++) {
+ Coord c(i, j);
+ if ( matrix()[c]==0 ) continue;
+ partialMoveBlock(c, QPoint(0, 0));
+ }
+
+ if (_next) {
+ Coord c = Piece::info().maxSize() + Coord(2, 2);
+ _next->resize(c.first * size, c.second * size);
+ _nextPiece->moveCenter();
+ }
+
+ FixedCanvasView::adjustSize();
+}
+
+BaseBoard::~BaseBoard()
+{
+ if ( graphic() ) {
+ setBlockInfo(0, 0); // destruct all sprites before deleting canvas
+ delete _next;
+ delete main;
+ delete sequences;
+ }
+}
+
+void BaseBoard::init(bool arcade)
+{
+ _arcade = arcade;
+ _arcadeStageDone = false;
+}
+
+void BaseBoard::start(const GTInitData &data)
+{
+ Q_ASSERT( graphic() );
+ if ( !_arcadeStageDone || _arcadeStage==bfactory->bbi.nbArcadeStages )
+ _arcadeStage = 0;
+ _arcadeStageDone = false;
+ state = Normal;
+ GenericTetris::start(data); // NB: the timer is started by updateLevel !
+ if (_arcade) arcadePrepare();
+}
+
+void BaseBoard::stop()
+{
+ timer.stop();
+ state = GameOver;
+}
+
+void BaseBoard::pause()
+{
+ Q_ASSERT( graphic() );
+ timer.stop();
+ _oldState = state;
+ state = Paused;
+ showBoard(false);
+}
+
+void BaseBoard::gameOver()
+{
+ stop();
+ emit gameOverSignal();
+}
+
+void BaseBoard::showCanvas(QCanvas *c, bool show)
+{
+ QCanvasItemList l = c->allItems();
+ QCanvasItemList::Iterator it;
+ for (it=l.begin(); it!=l.end(); ++it) {
+ if (show) (*it)->show();
+ else (*it)->hide();
+ }
+ c->update();
+}
+
+void BaseBoard::showBoard(bool show)
+{
+ showCanvas(main, show);
+}
+
+void BaseBoard::unpause()
+{
+ Q_ASSERT( graphic() );
+ showBoard(true);
+ state = _oldState;
+ startTimer();
+}
+
+void BaseBoard::updateRemoved(uint newRemoved)
+{
+ GenericTetris::updateRemoved(newRemoved);
+ emit removedUpdated();
+}
+
+void BaseBoard::updateScore(uint newScore)
+{
+ GenericTetris::updateScore(newScore);
+ emit scoreUpdated();
+}
+
+int BaseBoard::firstColumnBlock(uint col) const
+{
+ for (int j=firstClearLine()-1; j>=0; j--) {
+ Coord c(col, j);
+ if ( matrix()[c]!=0 ) return j;
+ }
+ return -1;
+}
+
+//-----------------------------------------------------------------------------
+void BaseBoard::_beforeRemove(bool first)
+{
+ if ( graphic() ) {
+ state = ( beforeRemove(first) ? BeforeRemove : Normal );
+ if ( state==BeforeRemove ) {
+ startTimer();
+ return;
+ }
+ }
+ remove();
+ _afterRemove(true);
+}
+
+void BaseBoard::remove()
+{
+ for (uint j=0; j<firstClearLine(); j++)
+ for (uint i=0; i<matrix().width(); i++) {
+ Coord c(i, j);
+ if ( matrix()[c]==0 || !toBeRemoved(c) ) continue;
+ removeBlock(c);
+ }
+ computeInfos();
+ if ( graphic() ) {
+ main->update();
+ KNotifyClient::event(winId(), "removed", i18n("Blocks removed"));
+ }
+}
+
+bool BaseBoard::doFall(bool doAll, bool first, bool lineByLine)
+{
+ Q_ASSERT( !lineByLine || !doAll );
+
+ if ( !doAll ) {
+ if (first) loop = 0;
+ else loop++;
+ }
+ bool final = (doAll || lineByLine
+ || loop==bfactory->bbi.nbFallStages);
+
+ for (uint i=0; i<matrix().width(); i++) {
+ // compute heights
+ // we must separate this computation since toFall() can depend
+ // directly on the disposition of blocks under the current one
+ // (for e.g. in kfouleggs)
+ // we do not rely on firstClearLine() here since this method is
+ // used in kfouleggs to make gift blocks fall down ...
+ uint h = 0;
+ QMemArray<uint> heights(matrix().height());
+ for (uint j=1; j<matrix().height(); j++) { // first line cannot fall
+ Coord src(i, j);
+ if ( toFall(src) ) h++;
+ heights[j] = h;
+ }
+
+ // do move
+ for (uint j=1; j<matrix().height(); j++) {
+ Coord src(i, j);
+ if( heights[j]==0 || matrix()[src]==0 ) continue;
+ if (lineByLine) final = false;
+ uint k = j - (lineByLine ? 1 : heights[j]);
+ Coord dest(i, k);
+ if ( final || lineByLine ) moveBlock(src, dest);
+ else partialBlockFall(src, dest);
+ }
+ }
+
+ if (final) computeInfos();
+ return final;
+}
+
+void BaseBoard::_afterRemove(bool first)
+{
+ AfterRemoveResult r = afterRemove(!graphic(), first);
+ switch (r) {
+ case Done:
+ state = Normal;
+ _afterAfterRemove();
+ return;
+ case NeedAfterRemove:
+ state = AfterRemove;
+ startTimer();
+ return;
+ case NeedRemoving:
+ _beforeRemove(true);
+ return;
+ }
+}
+
+BaseBoard::AfterRemoveResult BaseBoard::afterRemove(bool doAll, bool first)
+{
+ return (doFall(doAll, first, false) ? Done : NeedAfterRemove);
+}
+
+void BaseBoard::_afterAfterRemove()
+{
+ if ( isArcade() && arcadeDone()>=arcadeTodo() ) {
+ _arcadeStage++;
+ _arcadeStageDone = true;
+ gameOver();
+ return;
+ }
+ if ( !afterAfterRemove() ) gameOver();
+ else if ( graphic() ) startTimer();
+}
+
+bool BaseBoard::timeout()
+{
+ Q_ASSERT( graphic() );
+ if ( state==GameOver ) return true;
+ switch (state) {
+ case BeforeRemove: _beforeRemove(FALSE); break;
+ case AfterRemove: _afterRemove(FALSE); break;
+ default: return false;
+ }
+ main->update();
+ return true;
+}
+
+bool BaseBoard::startTimer()
+{
+ Q_ASSERT( graphic() );
+ if ( state==GameOver ) return true;
+ switch (state) {
+ case BeforeRemove:
+ timer.start(bfactory->bbi.beforeRemoveTime, true);
+ break;
+ case AfterRemove:
+ timer.start(bfactory->bbi.afterRemoveTime, true);
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+bool BaseBoard::beforeRemove(bool first)
+{
+ if (first) loop = 0;
+ else loop++;
+
+ for (uint j=0; j<firstClearLine(); j++)
+ for (uint i=0; i<matrix().width(); i++) {
+ Coord c(i, j);
+ if ( toBeRemoved(c) ) matrix()[c]->toggleLight();
+ }
+
+ return ( loop!=bfactory->bbi.nbToggles );
+}
+
+
+//-----------------------------------------------------------------------------
+void BaseBoard::partialBlockFall(const Coord &src, const Coord &dest)
+{
+ Q_ASSERT( loop<bfactory->bbi.nbFallStages );
+
+ float c = float(loop+1) / bfactory->bbi.nbFallStages * BasePrefs::blockSize();
+ int xdec = dest.first - src.first;
+ int ydec = src.second - dest.second;
+ QPoint p(int(xdec * c), int(ydec * c));
+ partialMoveBlock(src, p);
+}
+
+uint BaseBoard::findGroup(Square<int> &field, const Coord &c) const
+{
+ uint nb = 0;
+ _findGroup(field, c, nb, false);
+ return nb;
+}
+
+void BaseBoard::setGroup(Square<int> &field, const Coord &c, uint nb) const
+{
+ _findGroup(field, c, nb, true);
+}
+
+void BaseBoard::_findGroup(Square<int> &field, const Coord &c,
+ uint &nb, bool set) const
+{
+ if (!set) nb++;
+ field[c] = (set ? (int)nb : -1);
+ uint value = matrix()[c]->value();
+ CoordList n = matrix().neighbours(c, true, true);
+ for (CoordList::const_iterator i = n.begin(); i!=n.end(); ++i)
+ blockInGroup(field, *i, value, nb, set);
+}
+
+void BaseBoard::blockInGroup(Square<int> &field, const Coord &c, uint value,
+ uint &nb, bool set) const
+{
+ if ( matrix()[c]==0 ) return;
+ if ( matrix()[c]->value()!=value ) return;
+ if ( field[c]!=(set ? -1 : 0) ) return;
+ _findGroup(field, c, nb, set);
+}
+
+QMemArray<uint> BaseBoard::findGroups(Square<int> &field, uint minSize,
+ bool exitAtFirstFound) const
+{
+ field.fill(0);
+ QMemArray<uint> groups;
+ for (uint j=0; j<firstClearLine(); j++)
+ for (uint i=0; i<matrix().width(); i++) {
+ Coord c(i, j);
+ if ( matrix()[c]==0 || matrix()[c]->isGarbage() ) continue;
+ if ( field[c]!=0 ) continue;
+ uint nb = findGroup(field, c);
+ setGroup(field, c, nb);
+ if ( nb>=minSize ) {
+ uint s = groups.size();
+ groups.resize(s+1);
+ groups[s] = nb;
+ if (exitAtFirstFound) return groups;
+ }
+ }
+ return groups;
+}
+
+uint BaseBoard::drawCode(const Coord &c) const
+{
+ uint v = matrix()[c]->value();
+ uint code = 0;
+ for (uint i=0; i<Nb_Direction; i++) {
+ Coord nc = SquareBase::neighbour(c, DIRECTION_DATA[i].neighbour);
+ if ( !matrix().inside(nc) || matrix()[nc]==0
+ || matrix()[nc]->value()!=v ) continue;
+ code |= DIRECTION_DATA[i].direction;
+ }
+ return code;
+}
+
+void BaseBoard::computeNeighbours()
+{
+ for (uint j=0; j<firstClearLine(); j++)
+ for (uint i=0; i<matrix().width(); i++) {
+ Coord c(i, j);
+ if ( matrix()[c]==0 || matrix()[c]->isGarbage() ) continue;
+ matrix()[c]->sprite()->setFrame( drawCode(c) );
+ }
+}