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.
1045 lines
24 KiB
1045 lines
24 KiB
/*
|
|
* 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(<xtXpm_);
|
|
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;
|
|
}
|