summaryrefslogtreecommitdiffstats
path: root/ksokoban/PlayField.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'ksokoban/PlayField.cpp')
-rw-r--r--ksokoban/PlayField.cpp1044
1 files changed, 1044 insertions, 0 deletions
diff --git a/ksokoban/PlayField.cpp b/ksokoban/PlayField.cpp
new file mode 100644
index 00000000..4d98d309
--- /dev/null
+++ b/ksokoban/PlayField.cpp
@@ -0,0 +1,1044 @@
+/*
+ * ksokoban - a Sokoban game for KDE
+ * Copyright (C) 1998 Anders Widell <d95-awi@nada.kth.se>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdio.h>
+#include <assert.h>
+
+#include <qwidget.h>
+#include <qpixmap.h>
+#include <qkeycode.h>
+#include <kconfig.h>
+#include <kapplication.h>
+#include <klocale.h>
+#include <qpainter.h>
+#include <kmessagebox.h>
+#include <kglobalsettings.h>
+
+#include "PlayField.h"
+#include "ModalLabel.h"
+#include "LevelMap.h"
+#include "Move.h"
+#include "History.h"
+#include "PathFinder.h"
+#include "MapDelta.h"
+#include "MoveSequence.h"
+#include "StaticImage.h"
+#include "HtmlPrinter.h"
+#include "Bookmark.h"
+#include "LevelCollection.h"
+
+#include "PlayField.moc"
+
+PlayField::PlayField(QWidget *parent, const char *name, WFlags f)
+ : QWidget(parent, name, f|WResizeNoErase), imageData_(0), lastLevel_(-1),
+ moveSequence_(0), moveInProgress_(false), dragInProgress_(false),
+ xOffs_(0), yOffs_(0),
+ wheelDelta_(0),
+ levelText_(i18n("Level:")), stepsText_(i18n("Steps:")),
+ pushesText_(i18n("Pushes:")),
+ statusFont_(KGlobalSettings::generalFont().family(), 18, QFont::Bold), statusMetrics_(statusFont_) {
+
+ setFocusPolicy(QWidget::StrongFocus);
+ setFocus();
+ setBackgroundMode(Qt::NoBackground);
+ setMouseTracking(true);
+
+ highlightX_ = highlightY_ = 0;
+
+ KConfig *cfg = (KApplication::kApplication())->config();
+ cfg->setGroup("settings");
+
+ imageData_ = new StaticImage;
+
+ animDelay_ = cfg->readNumEntry("animDelay", 2);
+ if (animDelay_ < 0 || animDelay_ > 3) animDelay_ = 2;
+
+ history_ = new History;
+
+ background_.setPixmap(imageData_->background());
+ floor_ = QColor(0x66,0x66,0x66);
+
+ levelMap_ = new LevelMap;
+ mapDelta_ = new MapDelta(levelMap_);
+ mapDelta_->end();
+
+ levelChange();
+}
+
+PlayField::~PlayField() {
+ KConfig *cfg = (KApplication::kApplication())->config();
+ cfg->setGroup("settings");
+ cfg->writeEntry("animDelay", animDelay_, true, false, false);
+
+ delete mapDelta_;
+ delete history_;
+ delete levelMap_;
+ delete imageData_;
+}
+
+void
+PlayField::changeCursor(const QCursor* c) {
+ if (cursor_ == c) return;
+
+ cursor_ = c;
+ if (c == 0) unsetCursor();
+ else setCursor(*c);
+}
+
+int
+PlayField::level() const {
+ if (levelMap_ == 0) return 0;
+ return levelMap_->level();
+}
+
+const QString &
+PlayField::collectionName() {
+ static QString error = "????";
+ if (levelMap_ == 0) return error;
+ return levelMap_->collectionName();
+}
+
+int
+PlayField::totalMoves() const {
+ if (levelMap_ == 0) return 0;
+ return levelMap_->totalMoves();
+}
+
+int
+PlayField::totalPushes() const{
+ if (levelMap_ == 0) return 0;
+ return levelMap_->totalPushes();
+}
+
+void
+PlayField::levelChange() {
+ stopMoving();
+ stopDrag();
+ history_->clear();
+ setSize(width(), height());
+
+ updateLevelXpm();
+ updateStepsXpm();
+ updatePushesXpm();
+ highlight();
+}
+
+void
+PlayField::paintSquare(int x, int y, QPainter &paint) {
+ if (levelMap_->xpos() == x && levelMap_->ypos() == y) {
+ if (levelMap_->goal(x, y))
+ imageData_->saveman(paint, x2pixel(x), y2pixel(y));
+ else
+ imageData_->man(paint, x2pixel(x), y2pixel(y));
+ return;
+ }
+ if (levelMap_->empty(x, y)) {
+ if (levelMap_->floor(x, y)) {
+ if (levelMap_->goal(x, y))
+ imageData_->goal(paint, x2pixel(x), y2pixel(y));
+ else
+ paint.fillRect(x2pixel(x), y2pixel(y), size_, size_, floor_);
+ } else {
+ paint.fillRect(x2pixel(x), y2pixel(y), size_, size_, background_);
+ }
+ return;
+ }
+ if (levelMap_->wall(x, y)) {
+ imageData_->wall(paint, x2pixel(x), y2pixel(y), x+y*(MAX_X+1),
+ levelMap_->wallLeft(x, y),
+ levelMap_->wallRight(x, y));
+ return;
+ }
+
+
+ if (levelMap_->object(x, y)) {
+ if (highlightX_ == x && highlightY_ == y) {
+ if (levelMap_->goal(x, y))
+ imageData_->brightTreasure(paint, x2pixel(x), y2pixel(y));
+ else
+ imageData_->brightObject(paint, x2pixel(x), y2pixel(y));
+ } else {
+ if (levelMap_->goal(x, y))
+ imageData_->treasure(paint, x2pixel(x), y2pixel(y));
+ else
+ imageData_->object(paint, x2pixel(x), y2pixel(y));
+ }
+ return;
+ }
+}
+
+void
+PlayField::paintDelta() {
+ QPainter paint(this);
+
+ // the following line is a workaround for a bug in Qt 2.0.1
+ // (and possibly earlier versions)
+ paint.setBrushOrigin(0, 0);
+
+ for (int y=0; y<levelMap_->height(); y++) {
+ for (int x=0; x<levelMap_->width(); x++) {
+ if (mapDelta_->hasChanged(x, y)) paintSquare(x, y, paint);
+ }
+ }
+}
+
+
+
+void
+PlayField::paintEvent(QPaintEvent *e) {
+ QPainter paint(this);
+
+ // the following line is a workaround for a bug in Qt 2.0.1
+ // (and possibly earlier versions)
+ paint.setBrushOrigin(0, 0);
+
+ paint.setClipRegion(e->region());
+ paint.setClipping(true);
+
+ paintPainter(paint, e->rect());
+}
+
+void
+PlayField::paintPainterClip(QPainter &paint, int x, int y, int w, int h) {
+ QRect rect(x, y, w, h);
+
+ paint.setClipRect(rect);
+ paint.setClipping(true);
+ paintPainter(paint, rect);
+}
+
+void
+PlayField::paintPainter(QPainter &paint, const QRect &rect) {
+ if (size_ <= 0) return;
+ int minx = pixel2x(rect.x());
+ int miny = pixel2y(rect.y());
+ int maxx = pixel2x(rect.x()+rect.width()-1);
+ int maxy = pixel2y(rect.y()+rect.height()-1);
+
+ if (minx < 0) minx = 0;
+ if (miny < 0) miny = 0;
+ if (maxx >= levelMap_->width()) maxx = levelMap_->width()-1;
+ if (maxy >= levelMap_->height()) maxy = levelMap_->height()-1;
+
+ {
+ int x1, x2, y1, y2;
+ y1 = y2pixel(miny);
+ if (y1 > rect.y()) paint.fillRect(rect.x(), rect.y(), rect.width(), y1-rect.y(), background_);
+
+ int bot=rect.y()+rect.height();
+ if (bot > height()-collRect_.height()) bot = height()-collRect_.height();
+
+ y2 = y2pixel(maxy+1);
+ if (y2 < bot) paint.fillRect(rect.x(), y2, rect.width(), bot-y2, background_);
+
+ x1 = x2pixel(minx);
+ if (x1 > rect.x()) paint.fillRect(rect.x(), y1, x1-rect.x(), y2-y1, background_);
+
+ x2 = x2pixel(maxx+1);
+ if (x2 < rect.x()+rect.width()) paint.fillRect(x2, y1, rect.x()+rect.width()-x2, y2-y1, background_);
+
+ // paint.eraseRect
+ }
+
+ for (int y=miny; y<=maxy; y++) {
+ for (int x=minx; x<=maxx; x++) {
+ paintSquare(x, y, paint);
+ }
+ }
+
+ if (collRect_.intersects(rect)) paint.drawPixmap(collRect_.x(), collRect_.y(), collXpm_);
+ if (ltxtRect_.intersects(rect)) paint.drawPixmap(ltxtRect_.x(), ltxtRect_.y(), ltxtXpm_);
+ if (lnumRect_.intersects(rect)) paint.drawPixmap(lnumRect_.x(), lnumRect_.y(), lnumXpm_);
+ if (stxtRect_.intersects(rect)) paint.drawPixmap(stxtRect_.x(), stxtRect_.y(), stxtXpm_);
+ if (snumRect_.intersects(rect)) paint.drawPixmap(snumRect_.x(), snumRect_.y(), snumXpm_);
+ if (ptxtRect_.intersects(rect)) paint.drawPixmap(ptxtRect_.x(), ptxtRect_.y(), ptxtXpm_);
+ if (pnumRect_.intersects(rect)) paint.drawPixmap(pnumRect_.x(), pnumRect_.y(), pnumXpm_);
+}
+
+void
+PlayField::resizeEvent(QResizeEvent *e) {
+ setSize(e->size().width(), e->size().height());
+}
+
+void
+PlayField::mouseMoveEvent(QMouseEvent *e) {
+ lastMouseXPos_ = e->x();
+ lastMouseYPos_ = e->y();
+
+ if (!dragInProgress_) return highlight();
+
+ int old_x = dragX_, old_y = dragY_;
+
+ dragX_ = lastMouseXPos_ - mousePosX_;
+ dragY_ = lastMouseYPos_ - mousePosY_;
+
+ {
+ int x = pixel2x(dragX_ + size_/2);
+ int y = pixel2y(dragY_ + size_/2);
+ if (x >= 0 && x < levelMap_->width() &&
+ y >= 0 && y < levelMap_->height() &&
+ pathFinder_.canDragTo(x, y)) {
+ x = x2pixel(x);
+ y = y2pixel(y);
+
+ if (dragX_ >= x - size_/4 &&
+ dragX_ < x + size_/4 &&
+ dragY_ >= y - size_/4 &&
+ dragY_ < y + size_/4) {
+ dragX_ = x;
+ dragY_ = y;
+ }
+ }
+ }
+
+ if (dragX_ == old_x && dragY_ == old_y) return;
+
+ QRect rect(dragX_, dragY_, size_, size_);
+
+ dragXpm_.resize(size_, size_);
+
+ QPainter paint;
+ paint.begin(&dragXpm_);
+ paint.setBackgroundColor(backgroundColor());
+ paint.setBrushOrigin(- dragX_, - dragY_);
+ paint.translate((double) (- dragX_), (double) (- dragY_));
+ paintPainter(paint, rect);
+ paint.end();
+
+ dragImage_ = dragXpm_;
+ for (int yy=0; yy<size_; yy++) {
+ for (int xx=0; xx<size_; xx++) {
+ QRgb rgb1 = imageData_->objectImg().pixel(xx, yy);
+ int r1 = qRed(rgb1);
+ int g1 = qGreen(rgb1);
+ int b1 = qBlue(rgb1);
+ if (r1 != g1 || r1 != b1 || r1 == 255) {
+ QRgb rgb2 = dragImage_.pixel(xx, yy);
+ int r2 = qRed(rgb2);
+ int g2 = qGreen(rgb2);
+ int b2 = qBlue(rgb2);
+ r2 = (int) (0.75 * r1 + 0.25 * r2 + 0.5);
+ g2 = (int) (0.75 * g1 + 0.25 * g2 + 0.5);
+ b2 = (int) (0.75 * b1 + 0.25 * b2 + 0.5);
+ dragImage_.setPixel(xx, yy, qRgb(r2, g2, b2));
+ }
+ }
+ }
+
+ paint.begin(this);
+
+ // the following line is a workaround for a bug in Qt 2.0.1
+ // (and possibly earlier versions)
+ paint.setBrushOrigin(0, 0);
+
+ dragXpm_.convertFromImage(dragImage_,
+ OrderedDither|OrderedAlphaDither|
+ ColorOnly|AvoidDither);
+ paint.drawPixmap(dragX_, dragY_, dragXpm_);
+
+ {
+ int dx = dragX_ - old_x;
+ int dy = dragY_ - old_y;
+ int y2 = old_y;
+ if (dy > 0) {
+ paintPainterClip(paint, old_x, old_y, size_, dy);
+ // NOTE: clipping is now activated in the QPainter paint
+ y2 += dy;
+ } else if (dy < 0) {
+ paintPainterClip(paint, old_x, old_y+size_+dy, size_, -dy);
+ // NOTE: clipping is now activated in the QPainter paint
+ dy = -dy;
+ }
+ if (dx > 0) {
+ paintPainterClip(paint, old_x, y2, dx, size_-dy);
+ // NOTE: clipping is now activated in the QPainter paint
+ } else if (dx < 0) {
+ paintPainterClip(paint, old_x+size_+dx, y2, -dx, size_-dy);
+ // NOTE: clipping is now activated in the QPainter paint
+ }
+ }
+ paint.end();
+}
+
+void
+PlayField::highlight() {
+ // FIXME: the line below should not be needed
+ if (size_ == 0) return;
+
+ int x=pixel2x(lastMouseXPos_);
+ int y=pixel2y(lastMouseYPos_);
+
+ if (x < 0 || y < 0 || x >= levelMap_->width() || y >= levelMap_->height())
+ return;
+
+ if (x == highlightX_ && y == highlightY_) return;
+
+ if (pathFinder_.canDrag(x, y)) {
+ QPainter paint(this);
+
+ if (highlightX_ >= 0) {
+ int x = highlightX_, y = highlightY_;
+ highlightX_ = -1;
+ paintSquare(x, y, paint);
+ } else
+ changeCursor(&sizeAllCursor);
+
+ if (levelMap_->goal(x, y))
+ imageData_->brightTreasure(paint, x2pixel(x), y2pixel(y));
+ else
+ imageData_->brightObject(paint, x2pixel(x), y2pixel(y));
+ highlightX_ = x;
+ highlightY_ = y;
+ } else {
+ if (pathFinder_.canWalkTo(x, y)) changeCursor(&crossCursor);
+ else changeCursor(0);
+ if (highlightX_ >= 0) {
+ QPainter paint(this);
+
+ int x = highlightX_, y = highlightY_;
+ highlightX_ = -1;
+
+ paintSquare(x, y, paint);
+ }
+ }
+}
+
+void
+PlayField::stopMoving() {
+ killTimers();
+ delete moveSequence_;
+ moveSequence_ = 0;
+ moveInProgress_ = false;
+ updateStepsXpm();
+ updatePushesXpm();
+
+ QPainter paint(this);
+ paint.drawPixmap(snumRect_.x(), snumRect_.y(), snumXpm_);
+ paint.drawPixmap(pnumRect_.x(), pnumRect_.y(), pnumXpm_);
+
+ pathFinder_.updatePossibleMoves();
+}
+
+
+void
+PlayField::startMoving(Move *m) {
+ startMoving(new MoveSequence(m, levelMap_));
+}
+
+void
+PlayField::startMoving(MoveSequence *ms) {
+ static const int delay[4] = {0, 15, 35, 60};
+
+ assert(moveSequence_ == 0 && !moveInProgress_);
+ moveSequence_ = ms;
+ moveInProgress_ = true;
+ if (animDelay_) startTimer(delay[animDelay_]);
+ timerEvent(0);
+}
+
+void
+PlayField::timerEvent(QTimerEvent *) {
+ assert(moveInProgress_);
+ if (moveSequence_ == 0) {
+ killTimers();
+ moveInProgress_ = false;
+ return;
+ }
+
+ bool more=false;
+
+ mapDelta_->start();
+ if (animDelay_) more = moveSequence_->next();
+ else {
+ while (moveSequence_->next()) if (levelMap_->completed()) break;
+ more = true; // FIXME: clean this up
+ stopMoving();
+ }
+ mapDelta_->end();
+
+ if (more) {
+ paintDelta();
+ if (levelMap_->completed()) {
+ stopMoving();
+ ModalLabel::message(i18n("Level completed"), this);
+ nextLevel();
+ return;
+ }
+ } else stopMoving();
+}
+
+void
+PlayField::step(int _x, int _y) {
+ if (!canMoveNow()) return;
+
+ int oldX=levelMap_->xpos();
+ int oldY=levelMap_->ypos();
+ int x=oldX, y=oldY;
+
+ int dx=0, dy=0;
+ if (_x>oldX) dx=1;
+ if (_x<oldX) dx=-1;
+ if (_y>oldY) dy=1;
+ if (_y<oldY) dy=-1;
+
+ while (!(x==_x && y==_y) && levelMap_->step(x+dx, y+dy)) {
+ x += dx;
+ y += dy;
+ }
+
+ if (x!=oldX || y!=oldY) {
+ Move *m = new Move(oldX, oldY);
+ m->step(x, y);
+ m->finish();
+ history_->add(m);
+ m->undo(levelMap_);
+
+ startMoving(m);
+
+ }
+}
+
+void
+PlayField::push(int _x, int _y) {
+ if (!canMoveNow()) return;
+
+ int oldX=levelMap_->xpos();
+ int oldY=levelMap_->ypos();
+ int x=oldX, y=oldY;
+
+ int dx=0, dy=0;
+ if (_x>oldX) dx=1;
+ if (_x<oldX) dx=-1;
+ if (_y>oldY) dy=1;
+ if (_y<oldY) dy=-1;
+
+ while (!(x==_x && y==_y) && levelMap_->step(x+dx, y+dy)) {
+ x += dx;
+ y += dy;
+ }
+ int objX=x, objY=y;
+ while (!(x==_x && y==_y) && levelMap_->push(x+dx, y+dy)) {
+ x += dx;
+ y += dy;
+ }
+
+ if (x!=oldX || y!=oldY) {
+ Move *m = new Move(oldX, oldY);
+
+ if (objX!=oldX || objY!=oldY) m->step(objX, objY);
+
+ if (objX!=x || objY!=y) {
+ m->push(x, y);
+
+ objX += dx;
+ objY += dy;
+ }
+ m->finish();
+ history_->add(m);
+
+ m->undo(levelMap_);
+
+ startMoving(m);
+ }
+}
+
+void
+PlayField::keyPressEvent(QKeyEvent * e) {
+ int x=levelMap_->xpos();
+ int y=levelMap_->ypos();
+
+ switch (e->key()) {
+ case Key_Up:
+ if (e->state() & ControlButton) step(x, 0);
+ else if (e->state() & ShiftButton) push(x, 0);
+ else push(x, y-1);
+ break;
+ case Key_Down:
+ if (e->state() & ControlButton) step(x, MAX_Y);
+ else if (e->state() & ShiftButton) push(x, MAX_Y);
+ else push(x, y+1);
+ break;
+ case Key_Left:
+ if (e->state() & ControlButton) step(0, y);
+ else if (e->state() & ShiftButton) push(0, y);
+ else push(x-1, y);
+ break;
+ case Key_Right:
+ if (e->state() & ControlButton) step(MAX_X, y);
+ else if (e->state() & ShiftButton) push(MAX_X, y);
+ else push(x+1, y);
+ break;
+
+ case Key_Q:
+ KApplication::kApplication()->closeAllWindows();
+ break;
+
+ case Key_Backspace:
+ case Key_Delete:
+ if (e->state() & ControlButton) redo();
+ else undo();
+ break;
+
+#if 0
+ case Key_X:
+ levelMap_->random();
+ levelChange();
+ repaint(false);
+ break;
+
+ case Key_R:
+ level(levelMap_->level());
+ return;
+ break;
+ case Key_N:
+ nextLevel();
+ return;
+ break;
+ case Key_P:
+ previousLevel();
+ return;
+ break;
+ case Key_U:
+ undo();
+ return;
+ break;
+ case Key_I:
+ history_->redo(levelMap_);
+ repaint(false);
+ return;
+ break;
+
+ case Key_S:
+ {
+ QString buf;
+ history_->save(buf);
+ printf("%s\n", (char *) buf);
+ }
+ return;
+ break;
+
+ case Key_L:
+ stopMoving();
+ history_->clear();
+ level(levelMap_->level());
+ {
+ char buf[4096]="r1*D1*D1*r1*@r1*D1*";
+ //scanf("%s", buf);
+ history_->load(levelMap_, buf);
+ }
+ updateStepsXpm();
+ updatePushesXpm();
+ repaint(false);
+ return;
+ break;
+#endif
+
+
+ case Key_Print:
+ HtmlPrinter::printHtml(levelMap_);
+ break;
+
+ default:
+ e->ignore();
+ return;
+ break;
+ }
+}
+
+void
+PlayField::stopDrag() {
+ if (!dragInProgress_) return;
+
+ changeCursor(0);
+
+ QPainter paint(this);
+
+ // the following line is a workaround for a bug in Qt 2.0.1
+ // (and possibly earlier versions)
+ paint.setBrushOrigin(0, 0);
+
+ int x = highlightX_, y = highlightY_;
+ paintSquare(x, y, paint);
+
+ paintPainterClip(paint, dragX_, dragY_, size_, size_);
+ // NOTE: clipping is now activated in the QPainter paint
+ dragInProgress_ = false;
+
+}
+
+void
+PlayField::dragObject(int xpixel, int ypixel) {
+ int x=pixel2x(xpixel - mousePosX_ + size_/2);
+ int y=pixel2y(ypixel - mousePosY_ + size_/2);
+
+ if (x == highlightX_ && y == highlightY_) return;
+
+ printf("drag %d,%d to %d,%d\n", highlightX_, highlightY_, x, y);
+ pathFinder_.drag(highlightX_, highlightY_, x, y);
+ stopDrag();
+}
+
+
+void
+PlayField::mousePressEvent(QMouseEvent *e) {
+ if (!canMoveNow()) return;
+
+ if (dragInProgress_) {
+ if (e->button() == LeftButton) dragObject(e->x(), e->y());
+ else stopDrag();
+ return;
+ }
+
+ int x=pixel2x(e->x());
+ int y=pixel2y(e->y());
+
+ if (x < 0 || y < 0 || x >= levelMap_->width() || y >= levelMap_->height())
+ return;
+
+ if (e->button() == LeftButton && pathFinder_.canDrag(x, y)) {
+ QPainter paint(this);
+ changeCursor(&sizeAllCursor);
+
+ if (levelMap_->goal(x, y))
+ imageData_->brightTreasure(paint, x2pixel(x), y2pixel(y));
+ else
+ imageData_->brightObject(paint, x2pixel(x), y2pixel(y));
+ highlightX_ = x;
+ highlightY_ = y;
+ pathFinder_.updatePossibleDestinations(x, y);
+
+ dragX_ = x2pixel(x);
+ dragY_ = y2pixel(y);
+ mousePosX_ = e->x() - dragX_;
+ mousePosY_ = e->y() - dragY_;
+ dragInProgress_ = true;
+ }
+
+ Move *m;
+ switch (e->button()) {
+ case LeftButton:
+ m = pathFinder_.search(levelMap_, x, y);
+ if (m != 0) {
+ history_->add(m);
+
+ startMoving(m);
+ }
+ break;
+ case MidButton:
+ undo();
+ return;
+ break;
+ case RightButton:
+ push(x, y);
+ break;
+
+ default:
+ return;
+ }
+}
+
+void
+PlayField::wheelEvent(QWheelEvent *e) {
+ wheelDelta_ += e->delta();
+
+ if (wheelDelta_ >= 120) {
+ wheelDelta_ %= 120;
+ redo();
+ } else if (wheelDelta_ <= -120) {
+ wheelDelta_ = -(-wheelDelta_ % 120);
+ undo();
+ }
+}
+
+void
+PlayField::mouseReleaseEvent(QMouseEvent *e) {
+ if (dragInProgress_) dragObject(e->x(), e->y());
+}
+
+
+void
+PlayField::focusInEvent(QFocusEvent *) {
+ //printf("PlayField::focusInEvent\n");
+}
+
+void
+PlayField::focusOutEvent(QFocusEvent *) {
+ //printf("PlayField::focusOutEvent\n");
+}
+
+void
+PlayField::leaveEvent(QEvent *) {
+ stopDrag();
+}
+
+void
+PlayField::setSize(int w, int h) {
+ int sbarHeight = statusMetrics_.height();
+ int sbarNumWidth = statusMetrics_.boundingRect("88888").width()+8;
+ int sbarLevelWidth = statusMetrics_.boundingRect(levelText_).width()+8;
+ int sbarStepsWidth = statusMetrics_.boundingRect(stepsText_).width()+8;
+ int sbarPushesWidth = statusMetrics_.boundingRect(pushesText_).width()+8;
+
+ pnumRect_.setRect(w-sbarNumWidth, h-sbarHeight, sbarNumWidth, sbarHeight);
+ ptxtRect_.setRect(pnumRect_.x()-sbarPushesWidth, h-sbarHeight, sbarPushesWidth, sbarHeight);
+ snumRect_.setRect(ptxtRect_.x()-sbarNumWidth, h-sbarHeight, sbarNumWidth, sbarHeight);
+ stxtRect_.setRect(snumRect_.x()-sbarStepsWidth, h-sbarHeight, sbarStepsWidth, sbarHeight);
+ lnumRect_.setRect(stxtRect_.x()-sbarNumWidth, h-sbarHeight, sbarNumWidth, sbarHeight);
+ ltxtRect_.setRect(lnumRect_.x()-sbarLevelWidth, h-sbarHeight, sbarLevelWidth, sbarHeight);
+ collRect_.setRect(0, h-sbarHeight, ltxtRect_.x(), sbarHeight);
+
+ collXpm_.resize(collRect_.size());
+ ltxtXpm_.resize(ltxtRect_.size());
+ lnumXpm_.resize(lnumRect_.size());
+ stxtXpm_.resize(stxtRect_.size());
+ snumXpm_.resize(snumRect_.size());
+ ptxtXpm_.resize(ptxtRect_.size());
+ pnumXpm_.resize(pnumRect_.size());
+
+ h -= sbarHeight;
+
+ int cols = levelMap_->width();
+ int rows = levelMap_->height();
+
+ // FIXME: the line below should not be needed
+ if (cols == 0 || rows == 0) return;
+
+ int xsize = w / cols;
+ int ysize = h / rows;
+
+ if (xsize < 8) xsize = 8;
+ if (ysize < 8) ysize = 8;
+
+ size_ = imageData_->resize(xsize > ysize ? ysize : xsize);
+
+ xOffs_ = (w - cols*size_) / 2;
+ yOffs_ = (h - rows*size_) / 2;
+
+
+ updateCollectionXpm();
+ updateTextXpm();
+ updateLevelXpm();
+ updateStepsXpm();
+ updatePushesXpm();
+}
+
+void
+PlayField::nextLevel() {
+ if (levelMap_->level()+1 >= levelMap_->noOfLevels()) {
+ ModalLabel::message(i18n("\
+This is the last level in\n\
+the current collection."), this);
+ return;
+ }
+ if (levelMap_->level() >= levelMap_->completedLevels()) {
+ ModalLabel::message(i18n("\
+You have not completed\n\
+this level yet."), this);
+ return;
+ }
+
+ level(levelMap_->level()+1);
+ levelChange();
+ repaint(false);
+}
+
+void
+PlayField::previousLevel() {
+ if (levelMap_->level() <= 0) {
+ ModalLabel::message(i18n("\
+This is the first level in\n\
+the current collection."), this);
+ return;
+ }
+ level(levelMap_->level()-1);
+ levelChange();
+ repaint(false);
+}
+
+void
+PlayField::undo() {
+ if (!canMoveNow()) return;
+
+ startMoving(history_->deferUndo(levelMap_));
+}
+
+void
+PlayField::redo() {
+ if (!canMoveNow()) return;
+
+ startMoving(history_->deferRedo(levelMap_));
+}
+
+void
+PlayField::restartLevel() {
+ stopMoving();
+ history_->clear();
+ level(levelMap_->level());
+ updateStepsXpm();
+ updatePushesXpm();
+ repaint(false);
+}
+
+void
+PlayField::changeCollection(LevelCollection *collection) {
+ if (levelMap_->collection() == collection) return;
+ levelMap_->changeCollection(collection);
+ levelChange();
+ //erase(collRect_);
+ repaint(false);
+}
+
+void
+PlayField::updateCollectionXpm() {
+ if (collXpm_.isNull()) return;
+
+ QPainter paint(&collXpm_);
+ paint.setBrushOrigin(- collRect_.x(), - collRect_.y());
+ paint.fillRect(0, 0, collRect_.width(), collRect_.height(), background_);
+
+ paint.setFont(statusFont_);
+ paint.setPen(QColor(0,255,0));
+ paint.drawText(0, 0, collRect_.width(), collRect_.height(),
+ AlignLeft, collectionName());
+}
+
+void
+PlayField::updateTextXpm() {
+ if (ltxtXpm_.isNull()) return;
+
+ QPainter paint;
+
+ paint.begin(&ltxtXpm_);
+ paint.setBrushOrigin(- ltxtRect_.x(), - ltxtRect_.y());
+ paint.fillRect(0, 0, ltxtRect_.width(), ltxtRect_.height(), background_);
+ paint.setFont(statusFont_);
+ paint.setPen(QColor(128,128,128));
+ paint.drawText(0, 0, ltxtRect_.width(), ltxtRect_.height(), AlignLeft, levelText_);
+ paint.end();
+
+ paint.begin(&stxtXpm_);
+ paint.setBrushOrigin(- stxtRect_.x(), - stxtRect_.y());
+ paint.fillRect(0, 0, stxtRect_.width(), stxtRect_.height(), background_);
+ paint.setFont(statusFont_);
+ paint.setPen(QColor(128,128,128));
+ paint.drawText(0, 0, stxtRect_.width(), stxtRect_.height(), AlignLeft, stepsText_);
+ paint.end();
+
+ paint.begin(&ptxtXpm_);
+ paint.setBrushOrigin(- ptxtRect_.x(), - ptxtRect_.y());
+ paint.fillRect(0, 0, ptxtRect_.width(), ptxtRect_.height(), background_);
+ paint.setFont(statusFont_);
+ paint.setPen(QColor(128,128,128));
+ paint.drawText(0, 0, ptxtRect_.width(), ptxtRect_.height(), AlignLeft, pushesText_);
+ paint.end();
+}
+
+void
+PlayField::updateLevelXpm() {
+ if (lnumXpm_.isNull()) return;
+
+ QPainter paint(&lnumXpm_);
+ paint.setBrushOrigin(- lnumRect_.x(), - lnumRect_.y());
+ paint.fillRect(0, 0, lnumRect_.width(), lnumRect_.height(), background_);
+
+ QString str;
+ paint.setFont(statusFont_);
+ paint.setPen(QColor(255,0,0));
+ paint.drawText(0, 0, lnumRect_.width(), lnumRect_.height(),
+ AlignLeft, str.sprintf("%05d", level()+1));
+}
+
+void
+PlayField::updateStepsXpm() {
+ if (snumXpm_.isNull()) return;
+
+ QPainter paint(&snumXpm_);
+ paint.setBrushOrigin(- snumRect_.x(), - snumRect_.y());
+ paint.fillRect(0, 0, snumRect_.width(), snumRect_.height(), background_);
+
+ QString str;
+ paint.setFont(statusFont_);
+ paint.setPen(QColor(255,0,0));
+ paint.drawText(0, 0, snumRect_.width(), snumRect_.height(),
+ AlignLeft, str.sprintf("%05d", totalMoves()));
+}
+
+void
+PlayField::updatePushesXpm() {
+ if (pnumXpm_.isNull()) return;
+
+ QPainter paint(&pnumXpm_);
+ paint.setBrushOrigin(- pnumRect_.x(), - pnumRect_.y());
+ paint.fillRect(0, 0, pnumRect_.width(), pnumRect_.height(), background_);
+
+ QString str;
+ paint.setFont(statusFont_);
+ paint.setPen(QColor(255,0,0));
+ paint.drawText(0, 0, pnumRect_.width(), pnumRect_.height(),
+ AlignLeft, str.sprintf("%05d", totalPushes()));
+}
+
+
+void
+PlayField::changeAnim(int num)
+{
+ assert(num >= 0 && num <= 3);
+
+ animDelay_ = num;
+}
+
+// FIXME: clean up bookmark stuff
+
+// static const int bookmark_id[] = {
+// 0, 1, 8, 2, 9, 3, 5, 6, 7, 4
+// };
+
+void
+PlayField::setBookmark(Bookmark *bm) {
+ if (!levelMap_->goodLevel()) return;
+
+ if (collection()->id() < 0) {
+ KMessageBox::sorry(this, i18n("Sorry, bookmarks for external levels\n"
+ "is not implemented yet."));
+ return;
+ }
+
+ bm->set(collection()->id(), levelMap_->level(), levelMap_->totalMoves(), history_);
+}
+
+void
+PlayField::goToBookmark(Bookmark *bm) {
+ level(bm->level());
+ levelChange();
+ if (!bm->goTo(levelMap_, history_)) fprintf(stderr, "Warning: bad bookmark\n");
+ //updateLevelXpm();
+ updateStepsXpm();
+ updatePushesXpm();
+ repaint(false);
+}
+
+bool
+PlayField::canMoveNow() {
+ if (moveInProgress_) return false;
+ if (!levelMap_->goodLevel()) {
+ ModalLabel::message(i18n("This level is broken"), this);
+ return false;
+ }
+ return true;
+}