summaryrefslogtreecommitdiffstats
path: root/ksokoban
diff options
context:
space:
mode:
Diffstat (limited to 'ksokoban')
-rw-r--r--ksokoban/AUTHORS36
-rw-r--r--ksokoban/Bookmark.cpp108
-rw-r--r--ksokoban/Bookmark.h51
-rw-r--r--ksokoban/History.cpp131
-rw-r--r--ksokoban/History.h64
-rw-r--r--ksokoban/HtmlPrinter.cpp100
-rw-r--r--ksokoban/HtmlPrinter.h18
-rw-r--r--ksokoban/ImageData.cpp213
-rw-r--r--ksokoban/ImageData.h87
-rw-r--r--ksokoban/InternalCollections.cpp148
-rw-r--r--ksokoban/InternalCollections.h54
-rw-r--r--ksokoban/LevelCollection.cpp424
-rw-r--r--ksokoban/LevelCollection.h66
-rw-r--r--ksokoban/LevelMap.cpp205
-rw-r--r--ksokoban/LevelMap.h66
-rw-r--r--ksokoban/MainWindow.cpp364
-rw-r--r--ksokoban/MainWindow.h80
-rw-r--r--ksokoban/Makefile.am23
-rw-r--r--ksokoban/Map.cpp204
-rw-r--r--ksokoban/Map.h122
-rw-r--r--ksokoban/MapDelta.cpp63
-rw-r--r--ksokoban/MapDelta.h44
-rw-r--r--ksokoban/ModalLabel.cpp115
-rw-r--r--ksokoban/ModalLabel.h39
-rw-r--r--ksokoban/Move.cpp213
-rw-r--r--ksokoban/Move.h115
-rw-r--r--ksokoban/MoveSequence.cpp81
-rw-r--r--ksokoban/MoveSequence.h45
-rw-r--r--ksokoban/NEWS73
-rw-r--r--ksokoban/PathFinder.cpp176
-rw-r--r--ksokoban/PathFinder.h48
-rw-r--r--ksokoban/PlayField.cpp1044
-rw-r--r--ksokoban/PlayField.h149
-rw-r--r--ksokoban/Queue.h56
-rw-r--r--ksokoban/README33
-rw-r--r--ksokoban/StaticImage.cpp86
-rw-r--r--ksokoban/StaticImage.h33
-rw-r--r--ksokoban/TODO5
-rw-r--r--ksokoban/data/Makefile.am5
-rw-r--r--ksokoban/data/hi128-app-ksokoban.pngbin0 -> 20231 bytes
-rw-r--r--ksokoban/data/hi16-app-ksokoban.pngbin0 -> 907 bytes
-rw-r--r--ksokoban/data/hi22-app-ksokoban.pngbin0 -> 1412 bytes
-rw-r--r--ksokoban/data/hi32-app-ksokoban.pngbin0 -> 2451 bytes
-rw-r--r--ksokoban/data/hi48-app-ksokoban.pngbin0 -> 4582 bytes
-rw-r--r--ksokoban/data/hi64-app-ksokoban.pngbin0 -> 6908 bytes
-rw-r--r--ksokoban/data/ksokoban.desktop70
-rw-r--r--ksokoban/images/Makefile.am88
-rw-r--r--ksokoban/images/bin2c.c256
-rw-r--r--ksokoban/images/floor_common.inc19
-rw-r--r--ksokoban/images/goal.pngbin0 -> 7467 bytes
-rw-r--r--ksokoban/images/goal.pov34
-rw-r--r--ksokoban/images/halfstone_1.pngbin0 -> 4468 bytes
-rw-r--r--ksokoban/images/halfstone_1.pov17
-rw-r--r--ksokoban/images/halfstone_2.pngbin0 -> 4203 bytes
-rw-r--r--ksokoban/images/halfstone_2.pov17
-rw-r--r--ksokoban/images/halfstone_3.pngbin0 -> 3946 bytes
-rw-r--r--ksokoban/images/halfstone_3.pov17
-rw-r--r--ksokoban/images/halfstone_4.pngbin0 -> 4062 bytes
-rw-r--r--ksokoban/images/halfstone_4.pov17
-rw-r--r--ksokoban/images/man.pngbin0 -> 6298 bytes
-rw-r--r--ksokoban/images/man.pov5
-rw-r--r--ksokoban/images/man_common.inc59
-rw-r--r--ksokoban/images/object.pngbin0 -> 4955 bytes
-rw-r--r--ksokoban/images/object.pov36
-rw-r--r--ksokoban/images/saveman.pngbin0 -> 8949 bytes
-rw-r--r--ksokoban/images/saveman.pov5
-rw-r--r--ksokoban/images/starfield.pngbin0 -> 9618 bytes
-rw-r--r--ksokoban/images/stone_1.pngbin0 -> 7970 bytes
-rw-r--r--ksokoban/images/stone_1.pov17
-rw-r--r--ksokoban/images/stone_2.pngbin0 -> 7964 bytes
-rw-r--r--ksokoban/images/stone_2.pov17
-rw-r--r--ksokoban/images/stone_3.pngbin0 -> 7527 bytes
-rw-r--r--ksokoban/images/stone_3.pov17
-rw-r--r--ksokoban/images/stone_4.pngbin0 -> 7763 bytes
-rw-r--r--ksokoban/images/stone_4.pov17
-rw-r--r--ksokoban/images/stone_5.pngbin0 -> 8082 bytes
-rw-r--r--ksokoban/images/stone_5.pov17
-rw-r--r--ksokoban/images/stone_6.pngbin0 -> 8219 bytes
-rw-r--r--ksokoban/images/stone_6.pov17
-rw-r--r--ksokoban/images/stone_common.inc32
-rw-r--r--ksokoban/images/treasure.pngbin0 -> 7969 bytes
-rw-r--r--ksokoban/images/treasure.pov36
-rw-r--r--ksokoban/levels/Makefile.am11
-rw-r--r--ksokoban/levels/level.databin0 -> 68359 bytes
-rw-r--r--ksokoban/main.cpp86
85 files changed, 5894 insertions, 0 deletions
diff --git a/ksokoban/AUTHORS b/ksokoban/AUTHORS
new file mode 100644
index 00000000..3b450a4c
--- /dev/null
+++ b/ksokoban/AUTHORS
@@ -0,0 +1,36 @@
+------------------------------------------------------------------------
+ksokoban is written by:
+
+Anders Widell <awl@hem.passagen.se>
+
+------------------------------------------------------------------------
+The levels were taken from:
+
+xsokoban 3.3c for X-windows
+ www: http://xsokoban.lcs.mit.edu/xsokoban.html
+ author: Andrew Myers <andru@lcs.mit.edu>
+
+MacSokoban 3.0.3 for Macintosh
+ www: http://www.lysator.liu.se/~ingemar/games.html
+ author: Ingemar Ragnemalm <ingemar@lysator.liu.se>
+
+Sokoban 2.4 for Macintosh
+ www: http://members.aol.com/SokobanMac/
+ author: Scott Lindhurst <scottl@alumni.Princeton.EDU>
+
+------------------------------------------------------------------------
+The levels originally come from:
+
+Original the 50 original sokoban levels
+ made by Thinking rabbit Inc. in Japan
+Extra some more levels from xsokoban
+Still more by J. Franklin Mentzer <wryter@aol.com>
+MacTommy inventions by a guy called Tommy in Pennsylvania
+Yoshio's autogenerated by Yoshio Murase <yoshio@asahi.email.ne.jp>
+ see http://www.ne.jp/asahi/ai/yoshio/sokoban/main.htm
+For the kids by Ross (W.R.) Brown <ross@bnr.ca>
+Simple Sokoban simplified original levels
+ by Phil Shapiro <pshapiro@his.com>
+Dimitri & Yorick by Jacques Duthen <duthen@club-internet.fr>
+
+------------------------------------------------------------------------
diff --git a/ksokoban/Bookmark.cpp b/ksokoban/Bookmark.cpp
new file mode 100644
index 00000000..796d54b6
--- /dev/null
+++ b/ksokoban/Bookmark.cpp
@@ -0,0 +1,108 @@
+/*
+ * ksokoban - a Sokoban game for KDE
+ * Copyright (C) 1998 Anders Widell <awl@hem.passagen.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 "Bookmark.h"
+#include "History.h"
+#include "LevelMap.h"
+
+#include <qfile.h>
+
+#include <kapplication.h>
+
+#include <kglobal.h>
+#include <kstandarddirs.h>
+
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <assert.h>
+
+void
+Bookmark::fileName(QString &p) {
+ p = KGlobal::dirs()->saveLocation("appdata");
+
+ QString n;
+ n.setNum(number_);
+ p += "/bookmark" + n;
+}
+
+Bookmark::Bookmark(int _num) :
+ number_(_num), collection_(-1), level_(-1), moves_(0), data_("") {
+
+ QString p;
+ fileName(p);
+
+ FILE *file = fopen(p.latin1(), "r");
+ if (file == NULL) return;
+
+ char buf[4096];
+ buf[0] = '\0';
+ fgets (buf, 4096, file);
+ if (sscanf(buf, "%d %d %d", &collection_, &level_, &moves_) != 3) {
+ collection_ = level_ = -1;
+ data_ = "";
+ fclose(file);
+ return;
+ }
+
+ data_ = "";
+ int len;
+ while (!feof(file)) {
+ len = fread(buf, 1, 4095, file);
+ if (ferror(file)) break;
+ buf[len] = '\0';
+ data_ += buf;
+ }
+ fclose(file);
+
+ data_ = data_.stripWhiteSpace();
+}
+
+
+
+void
+Bookmark::set(int _collection, int _level, int _moves, History *_h) {
+ assert(_collection >= 0);
+ if (_collection < 0) return;
+
+ collection_ = _collection;
+ level_ = _level;
+ moves_ = _moves;
+
+ data_ = "";
+ _h->save(data_);
+
+ QString p;
+ fileName(p);
+ FILE *file = fopen(QFile::encodeName(p), "w");
+ if (file == NULL) return;
+ fprintf(file, "%d %d %d\n", collection_, level_, moves_);
+ fprintf(file, "%s\n", data_.latin1());
+ fclose(file);
+}
+
+bool
+Bookmark::goTo(LevelMap *_map, History *_h) {
+ return _h->load(_map, data_.latin1()) != 0;
+}
+
+
diff --git a/ksokoban/Bookmark.h b/ksokoban/Bookmark.h
new file mode 100644
index 00000000..30c5faff
--- /dev/null
+++ b/ksokoban/Bookmark.h
@@ -0,0 +1,51 @@
+/*
+ * ksokoban - a Sokoban game for KDE
+ * Copyright (C) 1998 Anders Widell <awl@hem.passagen.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
+ */
+
+#ifndef BOOKMARK_H
+#define BOOKMARK_H
+
+class History;
+class LevelMap;
+
+#include <qstring.h>
+
+class Bookmark {
+public:
+ Bookmark(int _num);
+
+ int collection() const { return collection_; }
+ int level() const { return level_; }
+ int moves() const { return moves_; }
+ //int pushes() { return pushes_; }
+
+ void set(int _collection, int _level, int _moves, History *_h);
+ bool goTo(LevelMap *_map, History *_h);
+
+private:
+ void fileName(QString &p);
+
+ int number_;
+ int collection_;
+ int level_;
+ int moves_;
+ //int pushes_;
+ QString data_;
+};
+
+#endif /* BOOKMARK_H */
diff --git a/ksokoban/History.cpp b/ksokoban/History.cpp
new file mode 100644
index 00000000..4c73827d
--- /dev/null
+++ b/ksokoban/History.cpp
@@ -0,0 +1,131 @@
+/*
+ * ksokoban - a Sokoban game for KDE
+ * Copyright (C) 1998 Anders Widell <awl@hem.passagen.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 <qptrlist.h>
+
+#include "History.h"
+#include "Move.h"
+#include "MoveSequence.h"
+#include "LevelMap.h"
+
+History::History() {
+ past_.setAutoDelete(true);
+ future_.setAutoDelete(true);
+}
+
+
+void
+History::add(Move *_m) {
+ future_.clear();
+ past_.append(_m);
+}
+
+void
+History::clear() {
+ past_.clear();
+ future_.clear();
+}
+
+void
+History::save(QString &_str) {
+ Move *m = past_.first();
+
+ while (m != 0) {
+ m->save(_str);
+ m = past_.next();
+ }
+ _str += '-';
+
+ m = future_.first();
+ while (m != 0) {
+ m->save(_str);
+ m = future_.next();
+ }
+}
+
+const char *
+History::load(LevelMap *map, const char *_str) {
+ Move *m;
+ int x = map->xpos();
+ int y = map->ypos();
+
+ clear();
+ while (*_str != '\0' && *_str != '-') {
+ m = new Move(x, y);
+ _str = m->load(_str);
+ if (_str == 0) return 0;
+ x = m->finalX();
+ y = m->finalY();
+ past_.append(m);
+ if (!m->redo(map)) {
+ //printf("redo failed: %s\n", _str);
+ //abort();
+ return 0;
+ }
+ }
+ if (*_str != '-') return 0;
+
+ _str++;
+ while (*_str != '\0') {
+ m = new Move(x, y);
+ _str = m->load(_str);
+ if (_str == 0) return 0;
+ x = m->finalX();
+ y = m->finalY();
+ future_.append(m);
+ }
+
+ return _str;
+}
+
+bool
+History::redo(LevelMap *map) {
+ if (future_.isEmpty()) return false;
+
+ Move *m=future_.take(0);
+ past_.append(m);
+ return m->redo(map);
+}
+
+MoveSequence *
+History::deferRedo(LevelMap *map) {
+ if (future_.isEmpty()) return 0;
+
+ Move *m=future_.take(0);
+ past_.append(m);
+ return new MoveSequence(m, map);
+}
+
+bool
+History::undo(LevelMap *map) {
+ if (past_.isEmpty()) return false;
+
+ Move *m = past_.take(past_.count ()-1);
+ future_.insert(0, m);
+ return m->undo(map);
+}
+
+MoveSequence *
+History::deferUndo(LevelMap *map) {
+ if (past_.isEmpty()) return 0;
+
+ Move *m = past_.take(past_.count()-1);
+ future_.insert(0, m);
+ return new MoveSequence(m, map, true);
+}
diff --git a/ksokoban/History.h b/ksokoban/History.h
new file mode 100644
index 00000000..c3db5194
--- /dev/null
+++ b/ksokoban/History.h
@@ -0,0 +1,64 @@
+/*
+ * ksokoban - a Sokoban game for KDE
+ * Copyright (C) 1998 Anders Widell <awl@hem.passagen.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
+ */
+
+#ifndef HISTORY_H
+#define HISTORY_H
+
+#include <qptrlist.h>
+#include <qstring.h>
+
+#include "Move.h"
+class MoveSequence;
+
+/**
+ * Maintains movement history
+ *
+ * @short Maintains game movement history
+ * @author Anders Widell <d95-awi@nada.kth.se>
+ * @version 0.1
+ * @see PlayField
+ */
+
+class History {
+private:
+ QPtrList<Move> past_;
+ QPtrList<Move> future_;
+
+protected:
+
+public:
+ History();
+ /**
+ * Add a move to the history. Deletes all currently undone moves.
+ */
+ void add(Move *_m);
+ /**
+ * Clear the history and delete all Move objects stored in it.
+ */
+ void clear();
+
+ void save(QString &_str);
+ const char *load(LevelMap *map, const char *_str);
+ bool redo(LevelMap *map);
+ MoveSequence *deferRedo(LevelMap *map);
+ bool undo(LevelMap *map);
+ MoveSequence *deferUndo(LevelMap *map);
+};
+
+#endif /* HISTORY_H */
diff --git a/ksokoban/HtmlPrinter.cpp b/ksokoban/HtmlPrinter.cpp
new file mode 100644
index 00000000..26e7207f
--- /dev/null
+++ b/ksokoban/HtmlPrinter.cpp
@@ -0,0 +1,100 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include "HtmlPrinter.h"
+
+void
+HtmlPrinter::wall (bool up, bool down, bool left, bool right)
+{
+ switch ( (up!=0) |
+ ((down!=0) << 1) |
+ ((left!=0) << 2) |
+ ((right!=0) << 3)) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ image ("vertiwall");
+ break;
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ image ("eastwall");
+ break;
+ case 8:
+ case 9:
+ case 10:
+ case 11:
+ image ("westwall");
+ break;
+ case 12:
+ case 13:
+ case 14:
+ case 15:
+ image ("horizwall");
+ break;
+
+ default:
+ abort ();
+ }
+}
+
+void
+HtmlPrinter::image (const char *name) {
+ printf ("<td><img src=%s.gif width=40 height=40><br clear=all>\n", name);
+}
+
+void
+HtmlPrinter::empty () {
+ printf ("<td>\n");
+}
+
+void
+HtmlPrinter::printSquare (LevelMap *lm, int x, int y) {
+ if (lm->xpos () == x && lm->ypos () == y) {
+ image (lm->goal (x, y) ? "saveman" : "man");
+ return;
+ }
+ if (lm->empty (x, y)) {
+ if (lm->floor (x, y)) {
+ image (lm->goal (x, y) ? "goal" : "floor");
+ } else {
+ empty ();
+ }
+ return;
+ }
+ if (lm->wall (x, y)) {
+ wall (lm->wallUp (x, y),
+ lm->wallDown (x, y),
+ lm->wallLeft (x, y),
+ lm->wallRight (x, y));
+ return;
+ }
+ if (lm->object (x, y)) {
+ image (lm->goal (x, y) ? "treasure" : "object");
+ return;
+ }
+}
+
+void
+HtmlPrinter::printHtml (LevelMap *lm) {
+ printf ("\
+<html>\n\
+<head>\n\
+<title>ksokoban level</title>\n\
+</head>\n\
+<body background=background.gif>\n\
+");
+ printf ("<table border=0 cellspacing=0 cellpadding=0>\n");
+ for (int y=0; y<lm->height(); y++) {
+ printf ("<tr>\n");
+ for (int x=0; x<lm->width(); x++) {
+ printSquare (lm, x, y);
+ }
+ }
+ printf ("\
+</table>\n\
+</body>\n\
+</html>\n\
+");
+}
diff --git a/ksokoban/HtmlPrinter.h b/ksokoban/HtmlPrinter.h
new file mode 100644
index 00000000..1f277607
--- /dev/null
+++ b/ksokoban/HtmlPrinter.h
@@ -0,0 +1,18 @@
+#ifndef HTMLPRINTER_H
+#define HTMLPRINTER_H
+
+#include "LevelMap.h"
+
+class HtmlPrinter {
+public:
+ static void printHtml (LevelMap *lm);
+
+protected:
+ static void wall (bool up, bool down, bool left, bool right);
+ static void image (const char *name);
+ static void empty ();
+ static void printSquare (LevelMap *lm, int x, int y);
+};
+
+
+#endif /* HTMLPRINTER_H */
diff --git a/ksokoban/ImageData.cpp b/ksokoban/ImageData.cpp
new file mode 100644
index 00000000..13040c47
--- /dev/null
+++ b/ksokoban/ImageData.cpp
@@ -0,0 +1,213 @@
+/*
+ * ksokoban - a Sokoban game for KDE
+ * Copyright (C) 1998 Anders Widell <awl@hem.passagen.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 "ImageData.h"
+
+#include <assert.h>
+#include <qpainter.h>
+#include <qpixmap.h>
+#include <qimage.h>
+#include <qcolor.h>
+
+ImageData::ImageData() : indexSize_(0), size_(0), halfSize_(0) {
+ random.setSeed(0);
+}
+
+ImageData::~ImageData() {
+}
+
+void
+ImageData::expandIndex(int size) {
+ size++;
+ assert(size < 2500);
+
+ upperLargeIndex_.resize(size);
+ lowerLargeIndex_.resize(size);
+ leftSmallIndex_.resize(size);
+ rightSmallIndex_.resize(size);
+
+ for (int i=indexSize_; i<size; i++) {
+ upperLargeIndex_[i] = random.getLong(LARGE_STONES);
+ lowerLargeIndex_[i] = random.getLong(LARGE_STONES);
+ leftSmallIndex_[i] = random.getLong(SMALL_STONES);
+ rightSmallIndex_[i] = random.getLong(SMALL_STONES);
+ }
+
+ indexSize_ = size;
+}
+
+const QPixmap &
+ImageData::upperLarge(int index) {
+ assert(index >= 0);
+ if (indexSize_ <= index) expandIndex(index);
+ return largeStone_xpm_[(unsigned char)upperLargeIndex_[index]];
+}
+
+const QPixmap &
+ImageData::lowerLarge(int index) {
+ assert(index >= 0);
+ if (indexSize_ <= index) expandIndex(index);
+ return largeStone_xpm_[(unsigned char)lowerLargeIndex_[index]];
+}
+
+const QPixmap &
+ImageData::leftSmall(int index) {
+ assert(index >= 0);
+ if (indexSize_ <= index) expandIndex(index);
+ return smallStone_xpm_[(unsigned char)leftSmallIndex_[index]];
+}
+
+const QPixmap &
+ImageData::rightSmall(int index) {
+ assert(index >= 0);
+ if (indexSize_ <= index) expandIndex(index);
+ return smallStone_xpm_[(unsigned char)rightSmallIndex_[index]];
+}
+
+int
+ImageData::resize(int size) {
+ assert(size > 0);
+ size &= ~1u;
+ if (size == size_) return size;
+
+ size_ = size;
+ halfSize_ = size/2;
+
+ for (int i=0; i<SMALL_STONES; i++) {
+ image2pixmap(images_[i].smoothScale(halfSize_, halfSize_), smallStone_xpm_[i]);
+// smallStone_xpm_[i].convertFromImage(images_[i].smoothScale(halfSize_, halfSize_), QPixmap::ColorOnly|QPixmap::DiffuseDither|QPixmap::DiffuseAlphaDither|QPixmap::AvoidDither);
+ }
+
+ for (int i=0; i<LARGE_STONES; i++) {
+ image2pixmap(images_[SMALL_STONES+i].smoothScale(size_, halfSize_), largeStone_xpm_[i]);
+// largeStone_xpm_[i].convertFromImage(images_[SMALL_STONES+i].smoothScale(size_, halfSize_) , QPixmap::ColorOnly|QPixmap::DiffuseDither|QPixmap::DiffuseAlphaDither|QPixmap::AvoidDither);
+ }
+
+ objectImg_ = images_[SMALL_STONES+LARGE_STONES].smoothScale(size_, size_);
+
+ // Use copy() because if the size is not changed, smoothScale is not
+ // really a copy
+ // Use {[Geometry] height=753 width=781} to test
+
+ if (objectImg_.width() == size_) objectImg_ = objectImg_.copy();
+
+ image2pixmap(objectImg_, otherPixmaps_[0], false);
+ brighten(objectImg_);
+ image2pixmap(objectImg_, brightObject_, false);
+
+ QImage img = images_[SMALL_STONES+LARGE_STONES+1].smoothScale(size_, size_);
+ if (img.width() == size_) img = img.copy();
+
+ image2pixmap(img, otherPixmaps_[1], false);
+ brighten(img);
+ image2pixmap(img, brightTreasure_, false);
+
+ for (int i=2; i<OTHER_IMAGES; i++) {
+ image2pixmap(images_[SMALL_STONES+LARGE_STONES+i].smoothScale(size_, size_), otherPixmaps_[i]);
+// otherPixmaps_[i].convertFromImage(images_[SMALL_STONES+LARGE_STONES+i].smoothScale(size_, size_), QPixmap::ColorOnly|QPixmap::OrderedDither|QPixmap::OrderedAlphaDither|QPixmap::AvoidDither);
+ }
+
+ return size_;
+}
+
+// Don't use DiffuseDither for the objects on the "floor" since
+// it gives spurious dots on the floor around them
+
+void
+ImageData::image2pixmap(QImage img, QPixmap& xpm, bool diffuse) {
+ xpm.convertFromImage(img,
+ (diffuse ?
+ (QPixmap::DiffuseDither|QPixmap::DiffuseAlphaDither) :
+ (QPixmap::OrderedDither|QPixmap::OrderedAlphaDither))|
+ QPixmap::ColorOnly|QPixmap::AvoidDither);
+}
+
+void
+ImageData::brighten(QImage& img) {
+ assert(img.depth() == 32);
+
+ for (int y=0; y<img.height(); y++) {
+ for (int x=0; x<img.width(); x++) {
+ QRgb rgb = img.pixel(x, y);
+ int r = qRed(rgb);
+ int g = qGreen(rgb);
+ int b = qBlue(rgb);
+
+ if (r > g && r > b) {
+ // only modify redish pixels
+
+ QColor col(r, g, b);
+ QColor lcol = col.light(130);
+
+ img.setPixel(x, y, lcol.rgb());
+ }
+ }
+ }
+}
+
+void
+ImageData::wall(QPainter &p, int x, int y, int index, bool left, bool right) {
+ if (left) p.drawPixmap(x, y, upperLarge(index-1), halfSize_);
+ else p.drawPixmap(x, y, leftSmall(index));
+
+ if (right) p.drawPixmap(x+halfSize_, y, upperLarge(index), 0, 0, halfSize_);
+ else p.drawPixmap(x+halfSize_, y, rightSmall(index));
+
+ p.drawPixmap(x, y+halfSize_, lowerLarge(index));
+}
+
+void
+ImageData::floor(QPainter &p, int x, int y) {
+ p.eraseRect(x, y, size_, size_);
+}
+
+void
+ImageData::goal(QPainter &p, int x, int y) {
+ p.drawPixmap(x, y, otherPixmaps_[2]);
+}
+
+void
+ImageData::man(QPainter &p, int x, int y) {
+ p.drawPixmap(x, y, otherPixmaps_[3]);
+}
+
+void
+ImageData::object(QPainter &p, int x, int y) {
+ p.drawPixmap(x, y, otherPixmaps_[0]);
+}
+
+void
+ImageData::saveman(QPainter &p, int x, int y) {
+ p.drawPixmap(x, y, otherPixmaps_[4]);
+}
+
+void
+ImageData::treasure(QPainter &p, int x, int y) {
+ p.drawPixmap(x, y, otherPixmaps_[1]);
+}
+
+void
+ImageData::brightObject(QPainter &p, int x, int y) {
+ p.drawPixmap(x, y, brightObject_);
+}
+
+void
+ImageData::brightTreasure(QPainter &p, int x, int y) {
+ p.drawPixmap(x, y, brightTreasure_);
+}
diff --git a/ksokoban/ImageData.h b/ksokoban/ImageData.h
new file mode 100644
index 00000000..4e57bc7d
--- /dev/null
+++ b/ksokoban/ImageData.h
@@ -0,0 +1,87 @@
+/*
+ * 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
+ */
+
+#ifndef IMAGEDATA_H
+#define IMAGEDATA_H
+
+#include <qimage.h>
+#include <qpixmap.h>
+#include <qcstring.h>
+
+#include <krandomsequence.h>
+
+class QPainter;
+
+#define SMALL_STONES 4
+#define LARGE_STONES 6
+#define OTHER_IMAGES 5
+#define NO_OF_IMAGES (SMALL_STONES + LARGE_STONES + OTHER_IMAGES)
+
+class
+ImageData {
+public:
+ virtual ~ImageData();
+
+ int resize(int size);
+ int size() { return size_; }
+
+ void wall(QPainter &p, int x, int y, int index, bool left, bool right);
+ void floor(QPainter &p, int x, int y);
+ void goal(QPainter &p, int x, int y);
+ void man(QPainter &p, int x, int y);
+ void object(QPainter &p, int x, int y);
+ void saveman(QPainter &p, int x, int y);
+ void treasure(QPainter &p, int x, int y);
+ void brightObject(QPainter &p, int x, int y);
+ void brightTreasure(QPainter &p, int x, int y);
+
+ const QPixmap &background() { return background_; }
+ const QImage& objectImg() const { return objectImg_; }
+
+protected:
+ ImageData();
+
+ void expandIndex(int size);
+ void image2pixmap(QImage img, QPixmap& xpm, bool diffuse=true);
+ void brighten(QImage& img);
+
+ const QPixmap &upperLarge(int index);
+ const QPixmap &lowerLarge(int index);
+ const QPixmap &leftSmall(int index);
+ const QPixmap &rightSmall(int index);
+
+ QImage images_[NO_OF_IMAGES];
+
+ QPixmap smallStone_xpm_[SMALL_STONES];
+ QPixmap largeStone_xpm_[LARGE_STONES];
+ QPixmap otherPixmaps_[OTHER_IMAGES];
+ QPixmap background_, brightObject_, brightTreasure_;
+ QImage objectImg_;
+
+ int indexSize_;
+ QByteArray upperLargeIndex_;
+ QByteArray lowerLargeIndex_;
+ QByteArray leftSmallIndex_;
+ QByteArray rightSmallIndex_;
+
+ int size_, halfSize_;
+ KRandomSequence random;
+};
+
+#endif /* IMAGEDATA_H */
diff --git a/ksokoban/InternalCollections.cpp b/ksokoban/InternalCollections.cpp
new file mode 100644
index 00000000..cdc3dbb3
--- /dev/null
+++ b/ksokoban/InternalCollections.cpp
@@ -0,0 +1,148 @@
+#include <klocale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#ifdef USE_LIBZ
+#include <zlib.h>
+#endif
+
+#include "InternalCollections.h"
+
+#ifndef LEVELS_INCLUDED
+#define LEVELS_INCLUDED 1
+#include "levels/data.c"
+#endif
+
+#define BUFSIZE (128*1024)
+
+// static const int collection_save_id[] = {
+// 0, 1, 3, 5, 9, 6, 7, 8, 2, 4
+// };
+
+static const int collection_save_id[] = {
+ 10, 11, 12, 13, 14
+};
+
+int
+InternalCollections::configCollection2Real (int collection) {
+ for (int i=0; i < (int) (sizeof (collection_save_id) / sizeof (int)); i++) {
+ if (collection_save_id[i] == collection) return i;
+ }
+ return 0;
+}
+
+int
+InternalCollections::realCollection2Config(int collection) {
+ assert(collection < (int) (sizeof (collection_save_id) / sizeof (int)));
+ return collection_save_id[collection];
+}
+
+QString
+InternalCollections::collectionName(int _level) {
+ switch (_level) {
+ case 0:
+ return i18n("Sasquatch");
+ break;
+
+ case 1:
+ return i18n("Mas Sasquatch");
+ break;
+
+ case 2:
+ return i18n("Sasquatch III");
+ break;
+
+ case 3:
+ return i18n("Microban (easy)");
+ break;
+
+ case 4:
+ return i18n("Sasquatch IV");
+ break;
+ }
+
+ assert(false);
+ return QString();
+}
+
+
+InternalCollections::InternalCollections() {
+ int datasize, levelnum=0;
+
+#ifdef USE_LIBZ
+ data_ = (char *) malloc(BUFSIZE);
+ if (data_ == NULL) abort();
+
+ datasize = BUFSIZE;
+ uncompress ((unsigned char *) data_, (long unsigned int *) &datasize, level_data_, sizeof (level_data_));
+ data_ = (char *) realloc(data_, datasize);
+ if (data_ == NULL) abort ();
+#else
+ datasize = sizeof (level_data_);
+ data_ = (char *) malloc(datasize);
+ if (data_ == NULL) abort();
+ memcpy(data_, level_data_, datasize);
+#endif
+
+ int start=0, end=0, name=0;
+ enum {NAME, DATA} state=NAME;
+ while (end < datasize) {
+ switch (state) {
+ case NAME:
+ if (data_[end] == '\n') {
+ data_[end] = '\0';
+ state = DATA;
+ }
+ end++;
+ start = end;
+ break;
+
+ case DATA:
+ if (isalpha(data_[end])) {
+// collections_.add(new LevelCollection(data_+start, end-start, data_+name, collection_save_id[levelnum]));
+ add(new LevelCollection(data_+start, end-start, collectionName(levelnum), collection_save_id[levelnum]));
+ //printf("Level found: '%s'\n", data_+name);
+ levelnum++;
+ name = end;
+ state = NAME;
+ }
+ end++;
+ break;
+
+ default:
+ assert(0);
+ }
+ }
+ if (state == DATA) {
+// collections_.add(new LevelCollection(data_+start, end-start, data_+name, collection_save_id[levelnum]));
+ add(new LevelCollection(data_+start, end-start, collectionName(levelnum), collection_save_id[levelnum]));
+ //printf("***Level found: '%s'\n", data_+name);
+ }
+ //printf("numlevels: %d/%d\n", levelnum+1, collections_.size());
+}
+
+InternalCollections::~InternalCollections() {
+ for (unsigned i=0; i<collections_.size(); i++) {
+ delete collections_[i];
+ }
+
+ free(data_);
+}
+
+int
+InternalCollections::collections() {
+ return collections_.size();
+}
+
+LevelCollection *
+InternalCollections::operator[](int n) {
+ return collections_[n];
+}
+
+void
+InternalCollections::add(LevelCollection* c) {
+ unsigned s = collections_.size();
+ collections_.resize(s + 1);
+ collections_.insert(s, c);
+}
diff --git a/ksokoban/InternalCollections.h b/ksokoban/InternalCollections.h
new file mode 100644
index 00000000..13759da8
--- /dev/null
+++ b/ksokoban/InternalCollections.h
@@ -0,0 +1,54 @@
+/*
+ * ksokoban - a Sokoban game for KDE
+ * Copyright (C) 1998-2000 Anders Widell <awl@hem.passagen.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
+ */
+
+#ifndef INTERNALCOLLECTIONS_H
+#define INTERNALCOLLECTIONS_H
+
+#include <assert.h>
+#include <qstring.h>
+#include <qptrvector.h>
+
+#include "LevelCollection.h"
+
+class InternalCollections {
+public:
+ InternalCollections();
+ ~InternalCollections();
+
+ static int toInternalId(int _id) {
+ if (_id < 10 || _id > 14) return 1000;
+ return _id - 10;
+ }
+
+ int collections();
+ LevelCollection *operator[](int n);
+
+private:
+ void add(LevelCollection* c);
+
+ static int configCollection2Real(int collection);
+ static int realCollection2Config(int collection);
+ static QString collectionName(int _level);
+
+ QPtrVector<LevelCollection> collections_;
+ char *data_;
+
+};
+
+#endif /* INTERNALCOLLECTIONS_H */
diff --git a/ksokoban/LevelCollection.cpp b/ksokoban/LevelCollection.cpp
new file mode 100644
index 00000000..7f5db852
--- /dev/null
+++ b/ksokoban/LevelCollection.cpp
@@ -0,0 +1,424 @@
+#include "LevelCollection.h"
+
+#include "Map.h"
+
+#include <qfile.h>
+#include <stdio.h>
+
+#include <assert.h>
+#include <unistd.h>
+#include <string.h>
+#include <kconfig.h>
+#include <kapplication.h>
+
+static inline unsigned long
+forward(unsigned long a, unsigned long b, unsigned long c, unsigned long d)
+{
+ unsigned long x=(a^b)&0xfffffffful;
+ return (((x<<c)|(x>>((32ul-c)&31ul)))*d)&0xfffffffful;
+}
+
+static inline unsigned long
+backward(unsigned long a, unsigned long b, unsigned long c, unsigned long d)
+{
+ unsigned long x=(a*b)&0xfffffffful;
+ return (((x<<c)|(x>>((32ul-c)&31ul)))^d)&0xfffffffful;
+}
+
+
+void
+LevelCollection::indexTextCollection() {
+ enum states {
+ BEFORE_NONE, BEFORE_VALID, BEFORE_INVALID,
+ DURING_NONE, DURING_VALID, DURING_INVALID
+ } state = BEFORE_NONE;
+
+ int levelstart=0, levelend=0;
+ for (unsigned pos=0; pos<(data_.size()-1); pos++) {
+ switch (state) {
+ case BEFORE_NONE:
+ switch (data_[pos]) {
+ case '#': case '.': case '$': case '+': case '*': case '@':
+ state = BEFORE_VALID;
+ break;
+
+ case ' ': case '\t': case '\r':
+ break;
+
+ case '\n':
+ levelstart = pos + 1;
+ break;
+
+ default:
+ state = BEFORE_INVALID;
+ break;
+ }
+ break;
+
+ case BEFORE_VALID:
+ switch (data_[pos]) {
+ case '#': case '.': case '$': case '+': case '*': case '@':
+ case ' ': case '\t': case '\r':
+ break;
+
+ case '\n':
+ addLevel(&data_[levelstart]);
+ levelend = levelstart;
+ state = DURING_NONE;
+ break;
+
+ default:
+ state = BEFORE_INVALID;
+ break;
+ }
+ break;
+
+ case BEFORE_INVALID:
+ switch (data_[pos]) {
+ case '\n':
+ levelstart = pos + 1;
+ state = BEFORE_NONE;
+ break;
+ }
+ break;
+
+ case DURING_NONE:
+ switch (data_[pos]) {
+ case '#': case '.': case '$': case '+': case '*': case '@':
+ state = DURING_VALID;
+ break;
+
+ case ' ': case '\t': case '\r':
+ break;
+
+ case '\n':
+ data_[levelend] = '\0';
+ levelstart = pos + 1;
+ state = BEFORE_NONE;
+ break;
+
+ default:
+ state = DURING_INVALID;
+ break;
+ }
+ break;
+
+ case DURING_VALID:
+ switch (data_[pos]) {
+ case '#': case '.': case '$': case '+': case '*': case '@':
+ case ' ': case '\t': case '\r':
+ break;
+
+ case '\n':
+ levelend = pos;
+ state = DURING_NONE;
+ break;
+
+ default:
+ state = DURING_INVALID;
+ break;
+ }
+ break;
+
+ case DURING_INVALID:
+ switch (data_[pos]) {
+ case '\n':
+ data_[levelend] = '\0';
+ levelstart = pos + 1;
+ state = BEFORE_NONE;
+ break;
+ }
+ break;
+
+ default:
+ assert(0);
+ }
+ }
+
+ if (state==DURING_NONE || state==DURING_INVALID) {
+ data_[levelend] = '\0';
+ }
+}
+
+void
+LevelCollection::loadPrefs() {
+ if (id_ >= 0) {
+ KConfig *cfg=(KApplication::kApplication())->config();
+ cfg->setGroup("settings");
+
+ QString key;
+ key.sprintf("level%d", id_);
+ level_ = cfg->readNumEntry(key, 0);
+
+ key.sprintf("status%d", id_);
+ unsigned long x = cfg->readUnsignedLongNumEntry(key, 0);
+
+ x = backward(x, 0xc1136a15ul, 0x12ul, 0x80ff0b94ul);
+ x = backward(x, 0xd38fd2ddul, 0x01ul, 0xd4d657b4ul);
+ x = backward(x, 0x59004eeful, 0x1eul, 0xf6c75e2cul);
+ x = backward(x, 0x366c3e25ul, 0x0aul, 0x61ebc208ul);
+ x = backward(x, 0x20a784c9ul, 0x15ul, 0x207d488bul);
+ x = backward(x, 0xc02864abul, 0x09ul, 0x709e62a3ul);
+ x = backward(x, 0xe2a60f19ul, 0x0eul, 0x8bb02c07ul);
+ x = backward(x, 0x3b0e11f3ul, 0x13ul, 0x608aef3ful);
+
+ completedLevels_ = x>>16 & 0x3ff;
+ if (!cfg->hasKey(key)) completedLevels_ = 0;
+ if (((x>>26) & 0x3ful) != (unsigned long) id_) completedLevels_ = 0;
+ if ((x & 0xfffful) != (unsigned long) getuid()) completedLevels_ = 0;
+ if (completedLevels_ > noOfLevels_) completedLevels_ = 0;
+
+ if (level_ > completedLevels_) level_ = completedLevels_;
+ if (level_ >= noOfLevels_) level_ = noOfLevels_-1;
+ if (level_ < 0) level_ = 0;
+ } else {
+ level_ = 0;
+ completedLevels_ = noOfLevels_;
+ }
+}
+
+void
+LevelCollection::addLevel(const char* _level) {
+ unsigned s = index_.size();
+ index_.resize(s + 1);
+ index_.insert(s, _level);
+}
+
+void
+LevelCollection::addData(const char* _data, unsigned _len) {
+ unsigned pos = data_.size();
+ data_.resize(pos + _len);
+ memcpy(data_.data() + pos, _data, _len);
+}
+
+void
+LevelCollection::addSeparator() {
+ unsigned pos = data_.size();
+ data_.resize(pos + 1);
+ data_[pos] = '\0';
+}
+
+LevelCollection::LevelCollection(const char *_def, int _len,
+ const QString &_name, int _id) :
+ level_(0), completedLevels_(0), noOfLevels_(0),
+ name_(_name), id_(_id) {
+
+ addData(_def, _len);
+ addSeparator();
+
+ indexTextCollection();
+
+ noOfLevels_ = index_.size();
+
+ loadPrefs();
+}
+
+LevelCollection::LevelCollection(const QString &_path, const QString &_name,
+ int _id) :
+ level_(0), completedLevels_(0), noOfLevels_(0),
+ name_(_name), path_(_path), id_(_id) {
+
+ char buf[1024];
+ int len;
+
+ QFile file(path_);
+ if (file.open(IO_Raw | IO_ReadOnly)) {
+ while ((len = file.readBlock(buf, 1024)) > 0) {
+ addData((const char *) buf, len);
+ }
+ file.close();
+ addSeparator();
+ }
+
+ indexTextCollection();
+
+ noOfLevels_ = index_.size();
+
+ loadPrefs();
+
+}
+
+LevelCollection::~LevelCollection() {
+ if (id_ >= 0) {
+ KConfig *cfg=(KApplication::kApplication())->config();
+ cfg->setGroup ("settings");
+
+ QString key;
+ key.sprintf("level%d", id_);
+ cfg->writeEntry(key, level_, true, false, false);
+ }
+}
+
+
+void
+LevelCollection::levelCompleted() {
+ if (completedLevels_ < (level_+1)) completedLevels_ = level_+1;
+
+ if (id_ >= 0) {
+ unsigned long x=(((unsigned long) getuid()) & 0xfffful);
+ x |= ((unsigned long) id_)<<26;
+ x |= ((unsigned long) completedLevels_)<<16;
+
+ x = forward(x, 0x608aef3ful, 0x0dul, 0xfb00ef3bul);
+ x = forward(x, 0x8bb02c07ul, 0x12ul, 0x2a37dd29ul);
+ x = forward(x, 0x709e62a3ul, 0x17ul, 0x23607603ul);
+ x = forward(x, 0x207d488bul, 0x0bul, 0xc31fd579ul);
+ x = forward(x, 0x61ebc208ul, 0x16ul, 0xbcffadadul);
+ x = forward(x, 0xf6c75e2cul, 0x02ul, 0xa2baa00ful);
+ x = forward(x, 0xd4d657b4ul, 0x1ful, 0x7e129575ul);
+ x = forward(x, 0x80ff0b94ul, 0x0eul, 0x92fc153dul);
+
+ QString key;
+ key.sprintf("status%d", id_);
+
+ KConfig *cfg=(KApplication::kApplication())->config();
+ cfg->setGroup("settings");
+ cfg->writeEntry(key, x, true, false, false);
+ cfg->sync();
+ }
+}
+
+
+void
+LevelCollection::level(int _level) {
+ assert(_level >= 0 && _level < noOfLevels_);
+
+ level_ = _level;
+ if (level_ > completedLevels_) level_ = completedLevels_;
+ if (level_ >= noOfLevels_) level_ = noOfLevels_ - 1;
+ if (level_ < 0) level_ = 0;
+}
+
+static int
+minX(const char *def) {
+ int min_x = 10000;
+
+ int x=0;
+ for (int pos=0; def[pos]; pos++) {
+ switch(def[pos]) {
+ case '\n':
+ x = 0;
+ break;
+
+ case ' ':
+ x++;
+ break;
+
+ case '\t':
+ x = (x+8) & ~7;
+ break;
+
+ case '\r':
+ break;
+
+ default:
+ if (x < min_x) min_x = x;
+ break;
+ }
+ }
+
+ return min_x == 10000 ? -1 : min_x;
+}
+
+
+bool
+LevelCollection::loadLevel(Map *_map) {
+ _map->clearMap();
+
+ const char *def = index_[level_];
+ bool goodMap = true;
+ int x=0, y=0, goalsLeft=0;
+
+ int min_x = minX(def);
+ if (min_x < 0) {
+ min_x = 0;
+ goodMap = false;
+ }
+
+
+ _map->xpos_ = -1;
+ _map->ypos_ = -1;
+
+ for (int pos=0; def[pos]; pos++) {
+ switch(def[pos]) {
+ case '\n':
+ y++;
+ x = 0;
+ break;
+
+ case ' ':
+ x++;
+ break;
+
+ case '\t':
+ x = (x+8) & ~7;
+ break;
+
+ case '@':
+ if (x-min_x > MAX_X || y > MAX_Y) goodMap = false;
+ else {
+ _map->xpos_ = x-min_x;
+ _map->ypos_ = y;
+ }
+ x++;
+ break;
+
+ case '$':
+ if (x-min_x > MAX_X || y > MAX_Y) goodMap = false;
+ else _map->map(x-min_x, y, OBJECT);
+ x++;
+ break;
+
+ case '.':
+ if (x-min_x > MAX_X || y > MAX_Y) goodMap = false;
+ else {
+ _map->map(x-min_x, y, GOAL);
+ goalsLeft++;
+ }
+ x++;
+ break;
+
+ case '#':
+ if (x-min_x > MAX_X || y > MAX_Y) goodMap = false;
+ else _map->map(x-min_x, y, WALL);
+ x++;
+ break;
+
+ case '+':
+ if (x-min_x > MAX_X || y > MAX_Y) goodMap = false;
+ else {
+ _map->xpos_ = x-min_x;
+ _map->ypos_ = y;
+ _map->map(x-min_x, y, GOAL);
+ goalsLeft++;
+ }
+ x++;
+ break;
+
+ case '*':
+ if (x-min_x > MAX_X || y > MAX_Y) goodMap = false;
+ else _map->map(x-min_x, y, OBJECT|GOAL);
+ x++;
+ break;
+
+ case '\r':
+ break;
+
+ default:
+ goodMap = false;
+ break;
+ }
+ }
+
+ if (_map->objectsLeft() != goalsLeft) goodMap = false;
+ if (_map->completed()) goodMap = false;
+
+ if (_map->badCoords(_map->xpos_, _map->ypos_)) goodMap = false;
+ else {
+ if (!_map->empty(_map->xpos_, _map->ypos_)) goodMap = false;
+ else if (!_map->fillFloor(_map->xpos_, _map->ypos_)) goodMap = false;
+ }
+
+ return goodMap;
+}
+
diff --git a/ksokoban/LevelCollection.h b/ksokoban/LevelCollection.h
new file mode 100644
index 00000000..f01d1316
--- /dev/null
+++ b/ksokoban/LevelCollection.h
@@ -0,0 +1,66 @@
+/*
+ * ksokoban - a Sokoban game for KDE
+ * Copyright (C) 1998,1999 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
+ */
+
+#ifndef LEVELCOLLECTION_H
+#define LEVELCOLLECTION_H
+
+#include <qstring.h>
+#include <qcstring.h>
+#include <qptrvector.h>
+
+class Map;
+
+class LevelCollection {
+public:
+ LevelCollection(const char *_def, int _len, const QString &_name, int _id=-1);
+ LevelCollection(const QString &_path, const QString &_name, int _id=-1);
+ ~LevelCollection();
+
+ const QString &name() const { return name_; }
+ int id() const { return id_; }
+ int level() const { return level_; }
+ void level(int _level);
+ void levelCompleted();
+ int completedLevels() const { return completedLevels_; }
+ int noOfLevels() const { return noOfLevels_; }
+ bool loadLevel(Map *_map);
+
+protected:
+ void indexTextCollection();
+ void loadPrefs();
+
+
+private:
+ void addLevel(const char* _level);
+ void addData(const char* _data, unsigned _len);
+ void addSeparator();
+
+ QPtrVector<const char> index_;
+ QByteArray data_;
+ //int dataLen_;
+
+ int level_;
+ int completedLevels_;
+ int noOfLevels_;
+ QString name_;
+ QString path_;
+ int id_;
+};
+
+#endif /* LEVELCOLLECTION_H */
diff --git a/ksokoban/LevelMap.cpp b/ksokoban/LevelMap.cpp
new file mode 100644
index 00000000..954dc588
--- /dev/null
+++ b/ksokoban/LevelMap.cpp
@@ -0,0 +1,205 @@
+/*
+ * 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 "config.h"
+
+#include <kconfig.h>
+#include <kapplication.h>
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+
+#ifdef USE_LIBZ
+#include <zlib.h>
+#endif
+
+#include "LevelMap.h"
+#include "LevelCollection.h"
+
+#define BUFSIZE (128*1024)
+
+const QString &
+LevelMap::collectionName() {
+ return collection_->name();
+}
+
+LevelMap::LevelMap () : collection_(0), totalMoves_(0), totalPushes_(0),
+ goodLevel_(false) {
+}
+
+LevelMap::~LevelMap () {
+}
+
+void
+LevelMap::changeCollection (LevelCollection *_collection)
+{
+ collection_ = _collection;
+ goodLevel_ = collection_->loadLevel(this);
+ totalMoves_ = totalPushes_ = 0;
+}
+
+int
+LevelMap::level () const {
+ if (collection_ == 0) return 0;
+ return collection_->level();
+}
+
+void
+LevelMap::level (int _level) {
+ assert(collection_ != 0);
+
+ collection_->level(_level);
+ goodLevel_ = collection_->loadLevel(this);
+
+ totalMoves_ = totalPushes_ = 0;
+}
+
+int
+LevelMap::noOfLevels () const {
+ assert(collection_ != 0);
+ return collection_->noOfLevels();
+}
+
+int
+LevelMap::completedLevels () const{
+ assert(collection_ != 0);
+ return collection_->completedLevels();
+}
+
+int
+LevelMap::distance (int x1, int y1, int x2, int y2) {
+ int d;
+
+ if (x2 > x1) d = x2-x1;
+ else d = x1-x2;
+
+ if (y2 > y1) d += y2-y1;
+ else d += y1-y2;
+
+ return d;
+}
+
+bool
+LevelMap::step (int _x, int _y) {
+ int oldX=xpos_, oldY=ypos_;
+
+ bool success = Map::step (_x, _y);
+
+ totalMoves_ += distance (oldX, oldY, xpos_, ypos_);
+
+ return success;
+}
+
+bool
+LevelMap::push (int _x, int _y) {
+ int oldX=xpos_, oldY=ypos_;
+
+ bool success = Map::push (_x, _y);
+
+ int d = distance (oldX, oldY, xpos_, ypos_);
+ totalMoves_ += d;
+ totalPushes_ += d;
+
+ if (completed ()) collection_->levelCompleted();
+
+ return success;
+}
+
+bool
+LevelMap::unstep (int _x, int _y) {
+ int oldX=xpos_, oldY=ypos_;
+
+ bool success = Map::unstep (_x, _y);
+
+ totalMoves_ -= distance (oldX, oldY, xpos_, ypos_);
+
+ return success;
+}
+
+bool
+LevelMap::unpush (int _x, int _y) {
+ int oldX=xpos_, oldY=ypos_;
+
+ bool success = Map::unpush (_x, _y);
+
+ int d = distance (oldX, oldY, xpos_, ypos_);
+ totalMoves_ -= d;
+ totalPushes_ -= d;
+
+ return success;
+}
+
+#if 0
+void
+LevelMap::random (void) {
+ printf ("start!\n");
+
+ minX_ = 0;
+ minY_ = 0;
+ maxX_ = MAX_X;
+ maxY_ = MAX_Y;
+ totalMoves_ = totalPushes_ = 0;
+ clearMap ();
+
+ xpos_ = 13;
+ ypos_ = 9;
+
+ KRandomSequence random(0);
+
+ for (int i=0; i<200; i++) {
+ map (xpos_, ypos_, FLOOR);
+
+ switch (random.getLong(4)) {
+ case 0:
+ if (ypos_ > 1) ypos_--; else i--;
+ break;
+
+ case 1:
+ if (ypos_ < MAX_Y-1) ypos_++; else i--;
+ break;
+
+ case 2:
+ if (xpos_ > 1) xpos_--; else i--;
+ break;
+
+ case 3:
+ if (xpos_ < MAX_X-1) xpos_++; else i--;
+ break;
+ }
+ }
+
+ for (int y=1; y<MAX_Y; y++) {
+ for (int x=1; x<MAX_X; x++) {
+ if (map (x, y) & FLOOR) {
+ if (!(map (x, y-1) & FLOOR)) map (x, y-1, WALL);
+ if (!(map (x, y+1) & FLOOR)) map (x, y+1, WALL);
+ if (!(map (x-1, y) & FLOOR)) map (x-1, y, WALL);
+ if (!(map (x+1, y) & FLOOR)) map (x+1, y, WALL);
+ }
+ }
+ }
+
+ printf ("klar!\n");
+ printMap ();
+}
+#endif
diff --git a/ksokoban/LevelMap.h b/ksokoban/LevelMap.h
new file mode 100644
index 00000000..ed45f1c0
--- /dev/null
+++ b/ksokoban/LevelMap.h
@@ -0,0 +1,66 @@
+/*
+ * 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
+ */
+
+#ifndef LEVELMAP_H
+#define LEVELMAP_H
+
+#include <assert.h>
+#include <qstring.h>
+
+#include "Map.h"
+class LevelCollection;
+
+//#define EXTERNAL_LEVEL 100
+
+class LevelMap : public Map {
+public:
+ LevelMap();
+ ~LevelMap();
+
+ LevelCollection *collection() const { return collection_; }
+ const QString &collectionName();
+ void changeCollection(LevelCollection *_collection);
+ int totalMoves() const { return totalMoves_; }
+ int totalPushes() const { return totalPushes_; }
+ void level(int _level);
+ int level() const;
+ int noOfLevels() const;
+ int completedLevels() const;
+ bool goodLevel() const { return goodLevel_; }
+
+ bool step(int _x, int _y);
+ bool push(int _x, int _y);
+ bool unstep(int _x, int _y);
+ bool unpush(int _x, int _y);
+
+ //void random();
+
+protected:
+ LevelCollection *collection_;
+
+
+private:
+ int totalMoves_;
+ int totalPushes_;
+ bool goodLevel_;
+
+ static int distance(int x1, int y1, int x2, int y2);
+};
+
+#endif /* LEVELMAP_H */
diff --git a/ksokoban/MainWindow.cpp b/ksokoban/MainWindow.cpp
new file mode 100644
index 00000000..63654f1c
--- /dev/null
+++ b/ksokoban/MainWindow.cpp
@@ -0,0 +1,364 @@
+/*
+ * ksokoban - a Sokoban game for KDE
+ * Copyright (C) 1998 Anders Widell <awl@hem.passagen.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 <kapplication.h>
+#include <kconfig.h>
+#include <kmenubar.h>
+#include <qpopupmenu.h>
+#include <qkeycode.h>
+#include <assert.h>
+#include <klocale.h>
+#include <qstring.h>
+#include <kfiledialog.h>
+#include <qframe.h>
+#include <kmessagebox.h>
+#include <kstandarddirs.h>
+#include <kio/netaccess.h>
+#include <kiconloader.h>
+#include <qiconset.h>
+#include <qdragobject.h>
+#include <kpopupmenu.h>
+#include <kurldrag.h>
+#include <kstdaccel.h>
+
+#include "MainWindow.h"
+#include "PlayField.h"
+#include "LevelCollection.h"
+
+#include "MainWindow.moc"
+
+void
+MainWindow::createCollectionMenu() {
+ collection_ = new QPopupMenu(0,"collection menu");
+ collection_->setCheckable(true);
+ //connect(collection_, SIGNAL(activated(int)), playField_, SLOT(changeCollection(int)));
+ connect(collection_, SIGNAL(activated(int)), this, SLOT(changeCollection(int)));
+
+ for (int i=0; i<internalCollections_.collections(); i++) {
+ collection_->insertItem(internalCollections_[i]->name(), i);
+ }
+ checkedCollection_ = 0;
+
+ KConfig *cfg=(KApplication::kApplication())->config();
+ cfg->setGroup("settings");
+ int id = cfg->readNumEntry("collection", 10);
+
+ currentCollection_ = 0;
+ for (int i=0; i<internalCollections_.collections(); i++) {
+ if (internalCollections_[i]->id() == id) currentCollection_ = i;
+ }
+
+ changeCollection(currentCollection_);
+}
+
+
+MainWindow::MainWindow() : KMainWindow(0), externalCollection_(0) {
+ int i;
+ QPixmap pixmap;
+
+ setEraseColor(QColor(0,0,0));
+
+ KConfig *cfg=(KApplication::kApplication())->config();
+ cfg->setGroup("Geometry");
+ int width = cfg->readNumEntry("width", 750);
+ int height = cfg->readNumEntry("height", 562);
+ resize(width, height);
+
+ playField_ = new PlayField(this, "playfield");
+ setCentralWidget(playField_);
+ playField_->show();
+
+ menu_ = new KMenuBar(this, "menubar" );
+
+ game_ = new QPopupMenu(0,"game menu");
+ pixmap = SmallIcon("fileopen");
+ game_->insertItem(QIconSet(pixmap), i18n("&Load Levels..."), this, SLOT(loadLevels()));
+ pixmap = SmallIcon("forward");
+ game_->insertItem(QIconSet(pixmap), i18n("&Next Level"), playField_, SLOT(nextLevel()), Key_N);
+ pixmap = SmallIcon("back");
+ game_->insertItem(QIconSet(pixmap), i18n("&Previous Level"), playField_, SLOT(previousLevel()), Key_P);
+ pixmap = SmallIcon("reload");
+ game_->insertItem(QIconSet(pixmap), i18n("Re&start Level"), playField_, SLOT(restartLevel()), Key_Escape);
+
+ createCollectionMenu();
+ game_->insertItem(i18n("&Level Collection"), collection_);
+
+ pixmap = SmallIcon("undo");
+ game_->insertItem(QIconSet(pixmap), i18n("&Undo"), playField_, SLOT(undo()),QKeySequence( (KStdAccel::undo()).toString()));
+ pixmap = SmallIcon("redo");
+ game_->insertItem(QIconSet(pixmap), i18n("&Redo"), playField_, SLOT(redo()), QKeySequence( (KStdAccel::redo()).toString()));
+ game_->insertSeparator();
+ pixmap = SmallIcon("exit");
+ game_->insertItem(QIconSet(pixmap), i18n("&Quit"), KApplication::kApplication(), SLOT(closeAllWindows()), QKeySequence( (KStdAccel::quit()).toString()));
+ menu_->insertItem(i18n("&Game"), game_);
+
+ animation_ = new QPopupMenu(0,"animation menu");
+ animation_->setCheckable(true);
+ connect(animation_, SIGNAL(activated(int)), this, SLOT(updateAnimMenu(int)));
+ connect(animation_, SIGNAL(activated(int)), playField_, SLOT(changeAnim(int)));
+ animation_->insertItem(i18n("&Slow"), 3);
+ animation_->insertItem(i18n("&Medium"), 2);
+ animation_->insertItem(i18n("&Fast"), 1);
+ animation_->insertItem(i18n("&Off"), 0);
+ checkedAnim_ = playField_->animDelay();
+ updateAnimMenu(checkedAnim_);
+ menu_->insertItem(i18n("&Animation"), animation_);
+
+ pixmap = SmallIcon("bookmark_add");
+ bookmarkMenu_ = new QPopupMenu(0,"bookmarks menu");
+ setBM_ = new QPopupMenu(0, "set bookmark menu");
+ setBM_->insertItem(QIconSet(pixmap), i18n("(unused)"), 1);
+ setBM_->setAccel(CTRL+Key_1, 1);
+ setBM_->insertItem(QIconSet(pixmap), i18n("(unused)"), 2);
+ setBM_->setAccel(CTRL+Key_2, 2);
+ setBM_->insertItem(QIconSet(pixmap), i18n("(unused)"), 3);
+ setBM_->setAccel(CTRL+Key_3, 3);
+ setBM_->insertItem(QIconSet(pixmap), i18n("(unused)"), 4);
+ setBM_->setAccel(CTRL+Key_4, 4);
+ setBM_->insertItem(QIconSet(pixmap), i18n("(unused)"), 5);
+ setBM_->setAccel(CTRL+Key_5, 5);
+ setBM_->insertItem(QIconSet(pixmap), i18n("(unused)"), 6);
+ setBM_->setAccel(CTRL+Key_6, 6);
+ setBM_->insertItem(QIconSet(pixmap), i18n("(unused)"), 7);
+ setBM_->setAccel(CTRL+Key_7, 7);
+ setBM_->insertItem(QIconSet(pixmap), i18n("(unused)"), 8);
+ setBM_->setAccel(CTRL+Key_8, 8);
+ setBM_->insertItem(QIconSet(pixmap), i18n("(unused)"), 9);
+ setBM_->setAccel(CTRL+Key_9, 9);
+ setBM_->insertItem(QIconSet(pixmap), i18n("(unused)"), 10);
+ setBM_->setAccel(CTRL+Key_0, 10);
+ connect(setBM_, SIGNAL(activated(int)), this, SLOT(setBookmark(int)));
+ bookmarkMenu_->insertItem(i18n("&Set Bookmark"), setBM_);
+
+ pixmap = SmallIcon("bookmark");
+ goToBM_ = new QPopupMenu(0, "go to bookmark menu");
+ goToBM_->insertItem(QIconSet(pixmap), i18n("(unused)"), 1);
+ goToBM_->setAccel(Key_1, 1);
+ goToBM_->insertItem(QIconSet(pixmap), i18n("(unused)"), 2);
+ goToBM_->setAccel(Key_2, 2);
+ goToBM_->insertItem(QIconSet(pixmap), i18n("(unused)"), 3);
+ goToBM_->setAccel(Key_3, 3);
+ goToBM_->insertItem(QIconSet(pixmap), i18n("(unused)"), 4);
+ goToBM_->setAccel(Key_4, 4);
+ goToBM_->insertItem(QIconSet(pixmap), i18n("(unused)"), 5);
+ goToBM_->setAccel(Key_5, 5);
+ goToBM_->insertItem(QIconSet(pixmap), i18n("(unused)"), 6);
+ goToBM_->setAccel(Key_6, 6);
+ goToBM_->insertItem(QIconSet(pixmap), i18n("(unused)"), 7);
+ goToBM_->setAccel(Key_7, 7);
+ goToBM_->insertItem(QIconSet(pixmap), i18n("(unused)"), 8);
+ goToBM_->setAccel(Key_8, 8);
+ goToBM_->insertItem(QIconSet(pixmap), i18n("(unused)"), 9);
+ goToBM_->setAccel(Key_9, 9);
+ goToBM_->insertItem(QIconSet(pixmap), i18n("(unused)"), 10);
+ goToBM_->setAccel(Key_0, 10);
+ connect(goToBM_, SIGNAL(activated(int)), this, SLOT(goToBookmark(int)));
+ bookmarkMenu_->insertItem(i18n("&Go to Bookmark"), goToBM_);
+ menu_->insertItem(i18n("&Bookmarks"), bookmarkMenu_);
+
+ for (i=1; i<=10; i++) {
+ bookmarks_[i-1] = new Bookmark(i);
+ updateBookmark(i);
+ }
+
+ help_ = helpMenu(QString::null, false);
+ menu_->insertSeparator();
+ menu_->insertItem(i18n("&Help"), help_);
+
+ menu_->show();
+
+ setAcceptDrops(true);
+}
+
+MainWindow::~MainWindow()
+{
+ KConfig *cfg=(KApplication::kApplication())->config();
+
+ cfg->setGroup("Geometry");
+ cfg->writeEntry("width", width());
+ cfg->writeEntry("height", height());
+
+ cfg->setGroup("settings");
+ cfg->writeEntry("collection", internalCollections_[checkedCollection_]->id());
+
+ for (int i=1; i<=10; i++) {
+ delete bookmarks_[i-1];
+ }
+
+
+ delete externalCollection_;
+
+ // The following line segfaults when linked against qt 1.44
+ //delete help_;
+ delete goToBM_;
+ delete setBM_;
+ delete bookmarkMenu_;
+ delete animation_;
+ delete collection_;
+ delete game_;
+ //delete menu_;
+
+ //delete playField_;
+}
+
+
+
+void
+MainWindow::focusInEvent(QFocusEvent *) {
+ playField_->setFocus();
+}
+
+void
+MainWindow::updateAnimMenu(int id) {
+ animation_->setItemChecked(checkedAnim_, false);
+ checkedAnim_ = id;
+ animation_->setItemChecked(checkedAnim_, true);
+}
+
+void
+MainWindow::updateBookmark(int num) {
+ int col = internalCollections_.toInternalId(bookmarks_[num-1]->collection());
+ int lev = bookmarks_[num-1]->level();
+ int mov = bookmarks_[num-1]->moves();
+
+ if (col < 0 || lev < 0) return;
+
+ QString name;
+ if (col >= 0 && col < internalCollections_.collections())
+ name = internalCollections_[col]->name();
+ else
+ name = i18n("(invalid)");
+ QString l;
+ l.setNum(lev+1);
+ name += " #" + l;
+ l.setNum(mov);
+ name += " (" + l + ")";
+
+ setBM_->changeItem(name, num);
+ goToBM_->changeItem(name, num);
+}
+
+void
+MainWindow::setBookmark(int id) {
+ assert(id >= 1 && id <= 10);
+ playField_->setBookmark(bookmarks_[id-1]);
+ updateBookmark(id);
+}
+
+void
+MainWindow::goToBookmark(int id) {
+ assert(id >= 1 && id <= 10);
+
+ Bookmark *bm = bookmarks_[id-1];
+ int collection = internalCollections_.toInternalId(bm->collection());
+ int level = bm->level();
+
+ if (collection < 0 || collection >= internalCollections_.collections()) return;
+ LevelCollection* colPtr = internalCollections_[collection];
+ if (colPtr == 0) return;
+ if (level < 0 || level >= colPtr->noOfLevels()) return;
+ if (level > colPtr->completedLevels()) return;
+
+ playField_->setUpdatesEnabled(false);
+ changeCollection(collection);
+ playField_->setUpdatesEnabled(true);
+ playField_->goToBookmark(bookmarks_[id-1]);
+}
+
+void
+MainWindow::changeCollection(int id)
+{
+ collection_->setItemChecked(checkedCollection_, false);
+ checkedCollection_ = id;
+ collection_->setItemChecked(checkedCollection_, true);
+
+ delete externalCollection_;
+ externalCollection_ = 0;
+ playField_->changeCollection(internalCollections_[id]);
+}
+
+void
+MainWindow::loadLevels() {
+ KConfig *cfg=(KApplication::kApplication())->config();
+ cfg->setGroup("settings");
+ QString lastFile = cfg->readPathEntry("lastLevelFile");
+
+ KURL result = KFileDialog::getOpenURL(lastFile, "*", this, i18n("Load Levels From File"));
+ if (result.isEmpty()) return;
+
+ openURL(result);
+}
+
+void
+MainWindow::openURL(KURL _url) {
+ KConfig *cfg=(KApplication::kApplication())->config();
+
+// int namepos = _url.path().findRev('/') + 1; // NOTE: findRev can return -1
+// QString levelName = _url.path().mid(namepos);
+ QString levelName = _url.fileName();
+
+ QString levelFile;
+ if (_url.isLocalFile()) {
+ levelFile = _url.path();
+ } else {
+// levelFile = locateLocal("appdata", "levels/" + levelName);
+ if(!KIO::NetAccess::download( _url, levelFile ) )
+ return;
+ }
+
+ LevelCollection *tmpCollection = new LevelCollection(levelFile, levelName);
+ KIO::NetAccess::removeTempFile(levelFile );
+
+ if (tmpCollection->noOfLevels() < 1) {
+ KMessageBox::sorry(this, i18n("No levels found in file"));
+ delete tmpCollection;
+ return;
+ }
+ if (_url.isLocalFile()) {
+ cfg->setGroup("settings");
+ cfg->writePathEntry("lastLevelFile", _url.path());
+ }
+
+ delete externalCollection_;
+ externalCollection_ = tmpCollection;
+
+ collection_->setItemChecked(checkedCollection_, false);
+ playField_->changeCollection(externalCollection_);
+
+
+}
+
+void
+MainWindow::dragEnterEvent(QDragEnterEvent* event) {
+ event->accept(KURLDrag::canDecode(event));
+}
+
+void
+MainWindow::dropEvent(QDropEvent* event) {
+ KURL::List urls;
+ if (KURLDrag::decode(event, urls)) {
+// kdDebug() << "MainWindow:Handling QUriDrag..." << endl;
+ if (urls.count() > 0) {
+ const KURL &url = urls.first();
+ openURL(url);
+ }
+ }
+}
diff --git a/ksokoban/MainWindow.h b/ksokoban/MainWindow.h
new file mode 100644
index 00000000..202afd13
--- /dev/null
+++ b/ksokoban/MainWindow.h
@@ -0,0 +1,80 @@
+/*
+ * 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
+ */
+
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include <kmainwindow.h>
+#include <kurl.h>
+#include "Bookmark.h"
+#include "InternalCollections.h"
+
+class KMenuBar;
+class PlayField;
+class QPopupMenu;
+class QFocusEvent;
+class QDragEnterEvent;
+class QDropEvent;
+class LevelCollection;
+
+class MainWindow : public KMainWindow {
+ Q_OBJECT
+public:
+ MainWindow();
+ ~MainWindow();
+
+ void openURL(KURL _url);
+
+public slots:
+ void changeCollection(int id);
+ void updateAnimMenu(int id);
+ void setBookmark(int id);
+ void goToBookmark(int id);
+
+ void loadLevels();
+
+protected:
+ void focusInEvent(QFocusEvent*);
+ void createCollectionMenu();
+ virtual void dragEnterEvent(QDragEnterEvent*);
+ virtual void dropEvent(QDropEvent*);
+
+private:
+ InternalCollections internalCollections_;
+ LevelCollection *externalCollection_;
+ KMenuBar *menu_;
+ PlayField *playField_;
+ Bookmark *bookmarks_[10];
+ int currentCollection_;
+
+ QPopupMenu *game_;
+ QPopupMenu *collection_;
+ QPopupMenu *animation_;
+ QPopupMenu *bookmarkMenu_;
+ QPopupMenu *setBM_;
+ QPopupMenu *goToBM_;
+ QPopupMenu *help_;
+ int checkedCollection_;
+ int checkedAnim_;
+
+ void updateBookmark(int num);
+
+};
+
+#endif /* MAINWINDOW_H */
diff --git a/ksokoban/Makefile.am b/ksokoban/Makefile.am
new file mode 100644
index 00000000..300d997e
--- /dev/null
+++ b/ksokoban/Makefile.am
@@ -0,0 +1,23 @@
+APPSDIR = $(kde_appsdir)/Games/TacticStrategy
+
+SUBDIRS=levels data images
+
+bin_PROGRAMS = ksokoban
+ksokoban_SOURCES = Bookmark.cpp History.cpp HtmlPrinter.cpp ImageData.cpp InternalCollections.cpp LevelCollection.cpp LevelMap.cpp MainWindow.cpp Map.cpp MapDelta.cpp ModalLabel.cpp Move.cpp MoveSequence.cpp PathFinder.cpp PlayField.cpp StaticImage.cpp main.cpp
+ksokoban_LDFLAGS = $(all_libraries) $(KDE_RPATH)
+ksokoban_LDADD = $(LIB_KIO)
+
+noinst_HEADERS = Bookmark.h History.h HtmlPrinter.h ImageData.h InternalCollections.h LevelCollection.h LevelMap.h MainWindow.h Map.h MapDelta.h ModalLabel.h Move.h MoveSequence.h PathFinder.h PlayField.h Queue.h StaticImage.h
+
+METASOURCES= MainWindow.moc ModalLabel.moc PlayField.moc
+
+INCLUDES = $(all_includes)
+
+EXTRA_DIST=AUTHORS NEWS README TODO
+
+# we need theese deps for the automatic generation of other deps
+StaticImage.o: images/data.c
+InternalCollections.o: levels/data.c
+
+messages:
+ $(XGETTEXT) *.cpp -o $(podir)/ksokoban.pot
diff --git a/ksokoban/Map.cpp b/ksokoban/Map.cpp
new file mode 100644
index 00000000..7dbb1cfa
--- /dev/null
+++ b/ksokoban/Map.cpp
@@ -0,0 +1,204 @@
+/*
+ * 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 <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <stdio.h>
+
+#include "Map.h"
+
+Map::Map() : xpos_(-1), ypos_(-1), width_(0), height_(0), objectsLeft_(-1) {
+}
+
+
+void
+Map::map (int x, int y, int val) {
+ assert (x>=0 && x<=MAX_X && y>=0 && y<=MAX_Y);
+ if ((map (x, y) & (OBJECT | GOAL)) == OBJECT) objectsLeft_--;
+ if ((val & (OBJECT | GOAL)) == OBJECT) objectsLeft_++;
+ currentMap_[y+1][x+1] = val;
+
+ if (val != 0) {
+ if (width_ <= x) width_ = x+1;
+ if (height_ <= y) height_ = y+1;
+ }
+}
+
+void
+Map::setMap (int x, int y, int bits) {
+ assert ((map (x, y) & bits) == 0);
+ if (goal (x, y) && ((bits & OBJECT) == OBJECT)) objectsLeft_--;
+ assert (objectsLeft_ >= 0);
+ currentMap_[y+1][x+1] |= bits;
+
+ if (bits != 0) {
+ if (width_ <= x) width_ = x+1;
+ if (height_ <= y) height_ = y+1;
+ }
+}
+
+void
+Map::clearMap (int x, int y, int bits) {
+ assert ((map (x, y) & bits) == bits);
+ if (goal (x, y) && ((bits & OBJECT) == OBJECT)) objectsLeft_++;
+ currentMap_[y+1][x+1] &= ~bits;
+}
+
+void
+Map::clearMap () {
+ memset (currentMap_, 0, (MAX_Y+3)*(MAX_X+3)*sizeof (char));
+ objectsLeft_ = 0;
+ width_ = height_ = 0;
+}
+
+bool
+Map::fillFloor (int x, int y) {
+ if (badCoords (x, y)) return false;
+ if ((currentMap_[y+1][x+1] & (WALL|FLOOR)) != 0) return true;
+
+ currentMap_[y+1][x+1] |= FLOOR;
+ bool a = fillFloor (x, y-1);
+ bool b = fillFloor (x, y+1);
+ bool c = fillFloor (x-1, y);
+ bool d = fillFloor (x+1, y);
+
+ return a && b && c && d;
+}
+
+bool
+Map::step (int _x, int _y) {
+ assert (!badCoords (xpos_, ypos_));
+ assert (empty (xpos_, ypos_));
+
+ int xd=0, yd=0;
+ if (_x < xpos_) xd = -1;
+ if (_x > xpos_) xd = 1;
+ if (_y < ypos_) yd = -1;
+ if (_y > ypos_) yd = 1;
+ if (badDelta (xd, yd) || badCoords (_x, _y)) return false;
+
+ int x=xpos_, y=ypos_;
+ do {
+ x += xd;
+ y += yd;
+ if (!empty (x, y)) return false;
+ } while (!(x==_x && y==_y));
+
+ xpos_ = _x;
+ ypos_ = _y;
+
+ return true;
+}
+
+bool
+Map::push (int _x, int _y) {
+ assert (!badCoords (xpos_, ypos_));
+ assert (empty (xpos_, ypos_));
+
+ int xd=0, yd=0;
+ if (_x < xpos_) xd = -1;
+ if (_x > xpos_) xd = 1;
+ if (_y < ypos_) yd = -1;
+ if (_y > ypos_) yd = 1;
+ if (badDelta (xd, yd) || badCoords (_x+xd, _y+yd)) return false;
+
+ int x=xpos_+xd, y=ypos_+yd;
+ if (!object (x, y)) return false;
+ if (!empty (_x+xd, _y+yd)) return false;
+
+ while (!(x==_x && y==_y)) {
+ x += xd;
+ y += yd;
+ if (!empty (x, y)) return false;
+ }
+
+ clearMap (xpos_+xd, ypos_+yd, OBJECT);
+ setMap (_x+xd, _y+yd, OBJECT);
+
+ xpos_ = _x;
+ ypos_ = _y;
+
+ return true;
+}
+
+bool
+Map::unstep (int _x, int _y) {
+ return Map::step (_x, _y);
+}
+
+bool
+Map::unpush (int _x, int _y) {
+ assert (!badCoords (xpos_, ypos_));
+ assert (empty (xpos_, ypos_));
+
+ int xd=0, yd=0;
+ if (_x < xpos_) xd = -1;
+ if (_x > xpos_) xd = 1;
+ if (_y < ypos_) yd = -1;
+ if (_y > ypos_) yd = 1;
+ if (badDelta (xd, yd) || badCoords (_x+xd, _y+yd)) return false;
+
+ int x=xpos_, y=ypos_;
+ if (!object (x-xd, y-yd)) return false;
+
+ do {
+ x += xd;
+ y += yd;
+ if (!empty (x, y)) return false;
+ } while (!(x==_x && y==_y));
+
+ clearMap (xpos_-xd, ypos_-yd, OBJECT);
+ setMap (_x-xd, _y-yd, OBJECT);
+
+ xpos_ = _x;
+ ypos_ = _y;
+
+ return true;
+}
+
+void
+Map::printMap(void) {
+ for (int y=0; y<height_; y++) {
+ for (int x=0; x<width_; x++) {
+ switch (map (x, y) & ~FLOOR) {
+ case WALL:
+ printf("#");
+ break;
+ case GOAL:
+ printf("%c", x==xpos_ && y==ypos_ ? '+' : '.');
+ break;
+ case OBJECT:
+ printf("$");
+ break;
+ case OBJECT|GOAL:
+ printf("*");
+ break;
+ case 0:
+ printf("%c", x==xpos_ && y==ypos_ ? '@' : ' ');
+ break;
+ default:
+ printf("<%X>", map(x,y)&FLOOR);
+ break;
+ }
+ }
+ printf ("\n");
+ }
+}
+
diff --git a/ksokoban/Map.h b/ksokoban/Map.h
new file mode 100644
index 00000000..32cd9ddc
--- /dev/null
+++ b/ksokoban/Map.h
@@ -0,0 +1,122 @@
+/*
+ * 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
+ */
+
+#ifndef MAP_H
+#define MAP_H
+
+#include <assert.h>
+
+#define MAX_X 49
+#define MAX_Y 49
+
+#define WALL 1
+#define GOAL 2
+#define OBJECT 4
+#define FLOOR 8
+
+class Map {
+ friend class MapDelta;
+ friend class LevelCollection;
+public:
+ Map();
+
+ bool completed () const { return objectsLeft_ <= 0; }
+
+ bool step (int _x, int _y);
+ bool push (int _x, int _y);
+ bool unstep (int _x, int _y);
+ bool unpush (int _x, int _y);
+
+
+ static bool badCoords (int _x, int _y) {
+ return _x<0 || _y<0 || _x>MAX_X || _y>MAX_Y;
+ }
+
+ static bool badDelta (int _xd, int _yd) {
+ return (_xd!=0 && _yd!=0) || (_xd==0 && _yd==0);
+ }
+
+ int xpos () const { return xpos_; }
+ int ypos () const { return ypos_; }
+
+ bool empty (int x, int y) {
+ assert (x>=0 && x<=MAX_X && y>=0 && y<=MAX_Y);
+ return (currentMap_[y+1][x+1] & (WALL|OBJECT)) == 0;
+ }
+ bool wall (int x, int y) {
+ assert (x>=0 && x<=MAX_X && y>=0 && y<=MAX_Y);
+ return (currentMap_[y+1][x+1] & WALL) != 0;
+ }
+ bool goal (int x, int y) {
+ assert (x>=0 && x<=MAX_X && y>=0 && y<=MAX_Y);
+ return (currentMap_[y+1][x+1] & GOAL) != 0;
+ }
+ bool object (int x, int y) {
+ assert (x>=0 && x<=MAX_X && y>=0 && y<=MAX_Y);
+ return (currentMap_[y+1][x+1] & OBJECT) != 0;
+ }
+ bool floor (int x, int y) {
+ assert (x>=0 && x<=MAX_X && y>=0 && y<=MAX_Y);
+ return (currentMap_[y+1][x+1] & FLOOR) != 0;
+ }
+
+ bool wallUp (int x, int y) {
+ assert (x>=0 && x<=MAX_X && y>=0 && y<=MAX_Y);
+ return (currentMap_[y ][x+1] & WALL) != 0;
+ }
+ bool wallDown (int x, int y) {
+ assert (x>=0 && x<=MAX_X && y>=0 && y<=MAX_Y);
+ return (currentMap_[y+2][x+1] & WALL) != 0;
+ }
+ bool wallLeft (int x, int y) {
+ assert (x>=0 && x<=MAX_X && y>=0 && y<=MAX_Y);
+ return (currentMap_[y+1][x ] & WALL) != 0;
+ }
+ bool wallRight (int x, int y) {
+ assert (x>=0 && x<=MAX_X && y>=0 && y<=MAX_Y);
+ return (currentMap_[y+1][x+2] & WALL) != 0;
+ }
+
+ void printMap ();
+
+ int width() const { return width_; }
+ int height() const { return height_; }
+
+protected:
+ int map (int x, int y) {
+ assert (x>=0 && x<=MAX_X && y>=0 && y<=MAX_Y);
+ return currentMap_[y+1][x+1];
+ }
+ void map (int x, int y, int val);
+
+ void setMap (int x, int y, int bits);
+ void clearMap (int x, int y, int bits);
+ void clearMap ();
+ bool fillFloor (int x, int y);
+ int objectsLeft () const { return objectsLeft_; }
+
+ int xpos_, ypos_;
+
+private:
+ char currentMap_[MAX_Y+3][MAX_X+3];
+ int width_, height_;
+ int objectsLeft_;
+};
+
+#endif /* MAP_H */
diff --git a/ksokoban/MapDelta.cpp b/ksokoban/MapDelta.cpp
new file mode 100644
index 00000000..bbf16ff4
--- /dev/null
+++ b/ksokoban/MapDelta.cpp
@@ -0,0 +1,63 @@
+/*
+ * 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 <string.h>
+
+#include "MapDelta.h"
+
+
+MapDelta::MapDelta (Map *m) {
+ source_ = m;
+ ended_ = true;
+ start ();
+}
+
+void
+MapDelta::start () {
+ assert (ended_);
+ ((Map &) *this) = *source_;
+
+#if 0
+ memcpy (map_, source_->currentMap_, (MAX_Y+3)*(MAX_X+3)*sizeof (int));
+ for (int y=1; y<MAX_Y+2; y++) {
+ for (int x=1; x<MAX_X+2; x++) {
+ map_[y][x] = source_->currentMap_[y][x];
+ }
+ }
+ xpos_ = source_->xpos_;
+ ypos_ = source_->ypos_;
+#endif
+
+ ended_ = false;
+}
+
+void
+MapDelta::end () {
+ assert (!ended_);
+ for (int y=0; y<=MAX_Y; y++) {
+ for (int x=0; x<=MAX_X; x++) {
+ map (x, y, map (x, y) != source_->map (x, y));
+ }
+ }
+ if (xpos_ != source_->xpos_ || ypos_ != source_->ypos_) {
+ map (xpos_, ypos_, 1);
+ map (source_->xpos_, source_->ypos_, 1);
+ }
+ ended_ = true;
+}
diff --git a/ksokoban/MapDelta.h b/ksokoban/MapDelta.h
new file mode 100644
index 00000000..c4e6763d
--- /dev/null
+++ b/ksokoban/MapDelta.h
@@ -0,0 +1,44 @@
+/*
+ * 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
+ */
+
+#ifndef MAPDELTA_H
+#define MAPDELTA_H
+
+#include <assert.h>
+
+#include "Map.h"
+
+class MapDelta : private Map {
+public:
+ MapDelta (Map *m);
+
+ void start ();
+ void end ();
+
+ bool hasChanged (int x, int y) {
+ assert (ended_);
+ return map (x, y) == 1;
+ }
+
+private:
+ Map *source_;
+ bool ended_;
+};
+
+#endif /* MAPDELTA_H */
diff --git a/ksokoban/ModalLabel.cpp b/ksokoban/ModalLabel.cpp
new file mode 100644
index 00000000..1ed55dcf
--- /dev/null
+++ b/ksokoban/ModalLabel.cpp
@@ -0,0 +1,115 @@
+/*
+ * ksokoban - a Sokoban game for KDE
+ * Copyright (C) 1998 Anders Widell <awl@hem.passagen.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 "ModalLabel.h"
+
+#include <qlabel.h>
+#include <qfont.h>
+#include <kapplication.h>
+#include <kglobalsettings.h>
+#include <qwidgetlist.h>
+#include <qstring.h>
+
+#include "ModalLabel.moc"
+
+ModalLabel::ModalLabel(const QString &text, QWidget *parent,
+ const char *name, WFlags f)
+ : QLabel(text, parent, name, f) {
+ QFont font(KGlobalSettings::generalFont().family(), 24, QFont::Bold);
+ QFontMetrics fontMet(font);
+
+ QString currentLine;
+ QRect bounds;
+ int lineLen, width=0, height=0;
+
+ for (int linePos=0; linePos < (int) text.length(); linePos += lineLen+1) {
+
+ lineLen = text.find('\n', linePos);
+ if (lineLen < 0) lineLen = text.length() - linePos;
+ else lineLen -= linePos;
+
+ currentLine = text.mid(linePos, lineLen);
+ bounds = fontMet.boundingRect(currentLine);
+
+ if (bounds.width() > width) width = bounds.width();
+ height += bounds.height();
+ }
+
+ width += 32;
+ height += 32;
+
+ if (width < 300) width = 300;
+ if (height < 75) height = 75;
+
+ setAlignment (AlignCenter);
+ setFrameStyle (QFrame::Panel | QFrame::Raised);
+ setLineWidth (4);
+ setFont (font);
+ move (parent->width ()/2 - width/2, parent->height ()/2 - height/2);
+ resize (width, height);
+ show ();
+
+ QWidgetList *list = QApplication::allWidgets();
+ QWidgetListIt it( *list );
+ while (it.current()) {
+ it.current()->installEventFilter (this);
+ ++it;
+ }
+ delete list;
+
+ completed_ = false;
+ startTimer (1000);
+}
+
+void
+ModalLabel::timerEvent (QTimerEvent *) {
+ completed_ = true;
+}
+
+bool
+ModalLabel::eventFilter (QObject *, QEvent *e) {
+ switch (e->type()) {
+ case QEvent::MouseButtonPress:
+ case QEvent::MouseButtonRelease:
+ case QEvent::MouseButtonDblClick:
+ case QEvent::MouseMove:
+ case QEvent::KeyPress:
+ case QEvent::KeyRelease:
+ case QEvent::Accel:
+ //case QEvent::DragEnter:
+ case QEvent::DragMove:
+ case QEvent::DragLeave:
+ case QEvent::Drop:
+ //case QEvent::DragResponse:
+
+ //kdDebug << "Ate event" << endl;
+ return true;
+ break;
+ default:
+ return false;
+ }
+}
+
+void
+ModalLabel::message (const QString &text, QWidget *parent) {
+ KApplication *app = KApplication::kApplication ();
+ ModalLabel cl (text, parent);
+
+ while (!cl.completed_) app->processOneEvent ();
+}
diff --git a/ksokoban/ModalLabel.h b/ksokoban/ModalLabel.h
new file mode 100644
index 00000000..4b35a82a
--- /dev/null
+++ b/ksokoban/ModalLabel.h
@@ -0,0 +1,39 @@
+/*
+ * 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
+ */
+
+#ifndef MODALLABEL_H
+#define MODALLABEL_H
+
+#include <qlabel.h>
+
+class ModalLabel : public QLabel {
+ Q_OBJECT
+public:
+ static void message (const QString &text, QWidget *parent);
+
+ void timerEvent (QTimerEvent *);
+ bool eventFilter (QObject *, QEvent *);
+ bool completed_;
+
+protected:
+ ModalLabel (const QString &text, QWidget *parent, const char *name=0, WFlags f=0);
+
+};
+
+#endif /* MODALLABEL_H */
diff --git a/ksokoban/Move.cpp b/ksokoban/Move.cpp
new file mode 100644
index 00000000..22773774
--- /dev/null
+++ b/ksokoban/Move.cpp
@@ -0,0 +1,213 @@
+/*
+ * 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 <assert.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "Move.h"
+#include "LevelMap.h"
+
+Move::Move (int _startX, int _startY) {
+ assert (_startX>=0 && _startX<=MAX_X && _startY>=0 && _startY<=MAX_Y);
+
+ moves_ = new unsigned short[400];
+ moves_[0] = _startX | (_startY<<8);
+ moveIndex_ = 1;
+ finished_ = false;
+
+#ifndef NDEBUG
+ lastX_ = _startX;
+ lastY_ = _startY;
+#endif
+}
+
+Move::~Move () {
+ delete [] moves_;
+}
+
+void
+Move::finish () {
+ assert (!finished_);
+ assert (moveIndex_ > 1);
+
+ unsigned short *newMoves = new unsigned short[moveIndex_];
+ memcpy (newMoves, moves_, moveIndex_*sizeof (unsigned short));
+ delete [] moves_;
+ moves_ = newMoves;
+
+ finished_ = true;
+}
+
+void
+Move::save (QString &s) {
+ static const char move1[] = "lrud";
+ static const char push1[] = "LRUD";
+ static const char move2[] = "wens";
+ static const char push2[] = "WENS";
+
+ assert (finished_);
+ int x=startX ();
+ int y=startY ();
+ int pos=1;
+
+ int x2, y2, dist=0;
+ int dir=-1;
+ bool push=false;
+ while (pos<moveIndex_) {
+ if (dir >= 0) s += push ? push1[dir] : move1[dir];
+
+ x2 = moves_[pos]&0x7f;
+ y2 = (moves_[pos]>>8)&0x7f;
+ push = (moves_[pos++]&0x80)==0x80;
+
+ if (x2<x) {
+ dir = 0;
+ dist = x-x2;
+ } else if (x2>x) {
+ dir = 1;
+ dist = x2-x;
+ } else if (y2<y) {
+ dir = 2;
+ dist = y-y2;
+ } else if (y2>y) {
+ dir = 3;
+ dist = y2-y;
+ } else {
+ assert (0);
+ }
+ assert (dist > 0);
+
+ if (dist > 1) {
+ if (dist>=10) {
+ s += '0' + (dist/10);
+ dist %= 10;
+ }
+ s += '0' + dist;
+ }
+
+ x = x2;
+ y = y2;
+ }
+
+ if (dir >= 0) s += push ? push2[dir] : move2[dir];
+}
+
+const char *
+Move::load (const char *s) {
+ assert (!finished_);
+ int x=finalX ();
+ int y=finalY ();
+
+ int dist;
+ bool last=false;
+ char c;
+ while ((c = *s++) != '\0') {
+ dist = 1;
+ if (c >= '0' && c <= '9') {
+ dist = c - '0';
+ c = *s++;
+ if (c >= '0' && c <= '9') {
+ dist = 10*dist + c - '0';
+ c = *s++;
+ }
+ }
+
+ switch (tolower (c)) {
+ case 'w':
+ last = true;
+ case 'l':
+ x -= dist;
+ break;
+ case 'e':
+ last = true;
+ case 'r':
+ x += dist;
+ break;
+ case 'n':
+ last = true;
+ case 'u':
+ y -= dist;
+ break;
+ case 's':
+ last = true;
+ case 'd':
+ y += dist;
+ break;
+
+ default:
+ //printf ("2><>%s\n", s);
+ //abort ();
+ return 0;
+ }
+
+ if (x<=0 || x>=MAX_X || y<=0 || y>=MAX_Y) {
+ //printf ("x: %d, y:%d ><>%s\n", x, y, s);
+ //abort ();
+
+ return 0;
+ }
+
+ if (isupper (c)) push (x, y);
+ else step (x, y);
+
+ if (last) break;
+ }
+ finish ();
+
+ return s;
+}
+
+bool
+Move::redo (LevelMap *map) {
+ assert (finished_);
+
+ for (int pos=1; pos<moveIndex_; pos++) {
+ int x = moves_[pos]&0x7f;
+ int y = (moves_[pos]>>8)&0x7f;
+ bool push = (moves_[pos]&0x80)==0x80;
+ bool ret;
+
+ if (push) ret = map->push (x, y);
+ else ret = map->step (x, y);
+
+ if (!ret) return false;
+ }
+
+ return true;
+}
+
+bool
+Move::undo (LevelMap *map) {
+ assert (finished_);
+
+ for (int pos=moveIndex_-2; pos>=0; --pos) {
+ int x = moves_[pos]&0x7f;
+ int y = (moves_[pos]>>8)&0x7f;
+ bool push = (moves_[pos+1]&0x80)==0x80;
+ bool ret;
+
+ if (push) ret = map->unpush (x, y);
+ else ret = map->unstep (x, y);
+
+ if (!ret) return false;
+ }
+
+ return true;
+}
diff --git a/ksokoban/Move.h b/ksokoban/Move.h
new file mode 100644
index 00000000..ad6b3eed
--- /dev/null
+++ b/ksokoban/Move.h
@@ -0,0 +1,115 @@
+/*
+ * 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
+ */
+
+#ifndef MOVE_H
+#define MOVE_H
+
+#include <assert.h>
+#include <qstring.h>
+
+#include "Map.h"
+class LevelMap;
+
+/**
+ * Holds information about a move
+ *
+ * The move can consist of several atomic steps and pushes. An atomic
+ * step/push is a step/push along a straight line. The reason why these are
+ * grouped together in a Move object is that they belong to the same logical
+ * move in the player's point of view. An undo/redo will undo/redo all the
+ * atomic moves in one step.
+ *
+ * @short Maintains game movement move
+ * @author Anders Widell <d95-awi@nada.kth.se>
+ * @version 0.1
+ * @see History
+ */
+
+class Move {
+ friend class MoveSequence;
+private:
+ unsigned short *moves_;
+ int moveIndex_;
+ bool finished_;
+
+#ifndef NDEBUG
+ int lastX_, lastY_;
+#endif
+
+
+public:
+ Move (int _startX, int _startY);
+ ~Move ();
+
+ /**
+ * Add an atomic move.
+ * NOTE: either (x != (previous x)) or (y != (previous y))
+ * must be true (but not both).
+ *
+ * @see LevelMap#move
+ *
+ * @param x x position of destination
+ * @param y y position of destination
+ */
+ void step (int _x, int _y) {
+#ifndef NDEBUG
+ assert (!finished_);
+ assert (_x>=0 && _x<=MAX_X && _y>=0 && _y<=MAX_Y);
+ assert (moveIndex_ < 400);
+ assert ((_x!=lastX_ && _y==lastY_) || (_x==lastX_ && _y!=lastY_));
+ lastX_ = _x;
+ lastY_ = _y;
+#endif
+
+ moves_[moveIndex_++] = _x | (_y<<8);
+ }
+
+ /**
+ * Same as move above, but used when an object is pushed.
+ *
+ * @see LevelMap#push
+ */
+ void push (int _x, int _y) {
+#ifndef NDEBUG
+ assert (!finished_);
+ assert (_x>=0 && _x<=MAX_X && _y>=0 && _y<=MAX_Y);
+ assert (moveIndex_ < 400);
+ assert ((_x!=lastX_ && _y==lastY_) || (_x==lastX_ && _y!=lastY_));
+ lastX_ = _x;
+ lastY_ = _y;
+#endif
+
+ moves_[moveIndex_++] = _x | (_y<<8) | 0x80;
+ }
+
+ void finish ();
+
+ int startX () const { return moves_[0]&0x7f; }
+ int startY () const { return (moves_[0]>>8)&0x7f; }
+ int finalX () const { return moves_[moveIndex_-1]&0x7f; }
+ int finalY () const { return (moves_[moveIndex_-1]>>8)&0x7f; }
+
+
+ void save (QString &_str);
+ const char *load (const char *_str);
+ bool redo (LevelMap *map);
+ bool undo (LevelMap *map);
+};
+
+#endif /* MOVE_H */
diff --git a/ksokoban/MoveSequence.cpp b/ksokoban/MoveSequence.cpp
new file mode 100644
index 00000000..b5a5c824
--- /dev/null
+++ b/ksokoban/MoveSequence.cpp
@@ -0,0 +1,81 @@
+/*
+ * 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 "MoveSequence.h"
+#include "LevelMap.h"
+#include "Move.h"
+
+MoveSequence::MoveSequence (Move *_move, LevelMap *_map, bool _undo) {
+ assert (_move->finished_);
+
+ move_ = _move;
+ map_ = _map;
+ undo_ = _undo;
+
+ if (undo_) {
+ pos_ = move_->moveIndex_-2;
+
+ xDest_ = x_ = move_->moves_[move_->moveIndex_-1]&0x7f;
+ yDest_ = y_ = (move_->moves_[move_->moveIndex_-1]>>8)&0x7f;
+ } else {
+ pos_ = 1;
+
+ xDest_ = x_ = move_->moves_[0]&0x7f;
+ yDest_ = y_ = (move_->moves_[0]>>8)&0x7f;
+ }
+
+ newStep ();
+}
+
+bool
+MoveSequence::newStep () {
+ if (pos_>=move_->moveIndex_ || pos_<0) return false;
+
+ xDest_ = move_->moves_[pos_]&0x7f;
+ yDest_ = (move_->moves_[pos_]>>8)&0x7f;
+ if (undo_) push_ = (move_->moves_[pos_+1]&0x80)==0x80;
+ else push_ = (move_->moves_[pos_]&0x80)==0x80;
+
+ xd_ = yd_ = 0;
+ if (xDest_ < x_) xd_ = -1;
+ if (xDest_ > x_) xd_ = 1;
+ if (yDest_ < y_) yd_ = -1;
+ if (yDest_ > y_) yd_ = 1;
+
+ if (undo_) pos_--;
+ else pos_++;
+
+ return true;
+}
+
+bool
+MoveSequence::next () {
+ if (x_ == xDest_ && y_ == yDest_ && !newStep ()) return false;
+
+ x_ += xd_;
+ y_ += yd_;
+
+ if (undo_) {
+ if (push_) return map_->unpush (x_, y_);
+ else return map_->unstep (x_, y_);
+ } else {
+ if (push_) return map_->push (x_, y_);
+ else return map_->step (x_, y_);
+ }
+}
diff --git a/ksokoban/MoveSequence.h b/ksokoban/MoveSequence.h
new file mode 100644
index 00000000..14cf5ac7
--- /dev/null
+++ b/ksokoban/MoveSequence.h
@@ -0,0 +1,45 @@
+/*
+ * 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
+ */
+
+#ifndef MOVESEQUENCE_H
+#define MOVESEQUENCE_H
+
+#include <assert.h>
+
+class Move;
+class LevelMap;
+
+class MoveSequence {
+public:
+ MoveSequence (Move *_move, LevelMap *_map, bool _undo=false);
+
+ bool newStep ();
+ bool next ();
+
+private:
+ LevelMap *map_;
+ Move *move_;
+ int pos_;
+ int x_, xDest_, y_, yDest_, xd_, yd_;
+ bool push_;
+ bool undo_;
+
+};
+
+#endif /* MOVESEQUENCE_H */
diff --git a/ksokoban/NEWS b/ksokoban/NEWS
new file mode 100644
index 00000000..bf717f78
--- /dev/null
+++ b/ksokoban/NEWS
@@ -0,0 +1,73 @@
+-------------------------------------------------------------------------------
+version 0.3.0: Date 1999-07-13
+
+* Graphics:
+ - Switched to one single image resolution
+ - Converted images to PNG and JPEG formats
+ - The window can now be resized freely - graphics is scaled to fit window
+ - Graphics now looks MUCH better on 256 colour displays
+ - Improved wall graphics (several stones with different textures)
+ - Removed status bar - now draws info directly in widget (not perfect yet)
+
+* New experimental feature:
+ - It is now possible to load external level collections from text files
+
+* Some changes related to KDE 2.0 / Qt 2.0
+ - replaced many char * with QString
+ - uses KStandardDirs instead of localkdedir
+
+* Some minor bugfixes/cleanups
+
+-------------------------------------------------------------------------------
+version 0.2.2: Date 1998-11-06
+
+* Bugfix: "Animation off" now works again
+
+* Possibly fixed bug: Settings should now always be saved properly
+ I haven't been able to reproduce this bug, but it has been reported
+ that the settings would sometimes not be saved to the right section
+ in the config file. Thanks to Tore Skaug <toresk@pvv.ntnu.no> for
+ reporting this.
+
+* Now uses the zlib check in acinclude
+
+-------------------------------------------------------------------------------
+version 0.2.1: Date 1998-10-25
+
+* Bugfix: Fixed failed assertion that showed up if you changed to small
+ graphics and restarted the game.
+ Thanks to Bernd Weber <bernd.weber@systemhaus.net> for reporting
+ this.
+
+* No longer requires zlib. It is still used if found, though.
+
+-------------------------------------------------------------------------------
+version 0.2.0: Date 1998-10-15
+
+New features:
+
+* Bookmarks
+* Animation speed menu
+* Status bar
+* Internationalisation
+
+-------------------------------------------------------------------------------
+version 0.1.2: ksokoban imported to CVS. Date 1998-08-30
+
+-------------------------------------------------------------------------------
+version 0.1.1: bugfix release. Date 1998-08-25
+
+* BUGFIX: ksokoban now ignores mouseclicks while a move is in progress.
+ Previously such a click would cause a memory leak and a corrupted
+ undo/redo history (or a failed assertion if debugging was turned on).
+ Thanks to Natali Giuliano <natali@stone.trew.it> for reporting this.
+
+* ksokoban should now work with older zlib & Qt libraries.
+ I have tested it with zlib 1.0.3 and Qt 1.33
+
+* Detects the old gcc 2.7 and turns off optimisations if it is found.
+ It might work with the gcc 2.7 now. No promises, though.
+ I can only test it with gcc 2.8 and egcs/pgcc.
+
+-------------------------------------------------------------------------------
+version 0.1.0: initial release. Date 1998-08-22
diff --git a/ksokoban/PathFinder.cpp b/ksokoban/PathFinder.cpp
new file mode 100644
index 00000000..a7d8bd4d
--- /dev/null
+++ b/ksokoban/PathFinder.cpp
@@ -0,0 +1,176 @@
+/*
+ * 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 "PathFinder.h"
+#include "LevelMap.h"
+#include "Queue.h"
+#include "Move.h"
+
+void
+PathFinder::BFS (int _x, int _y) {
+ Queue<int, 10> xq;
+ Queue<int, 10> yq;
+ Queue<int, 10> dq;
+ int x, y, d;
+
+ xq.enqueue (_x);
+ yq.enqueue (_y);
+ dq.enqueue (1);
+
+ while (!xq.empty ()) {
+ x = xq.dequeue ();
+ y = yq.dequeue ();
+ d = dq.dequeue ();
+
+ if (x<0 || x>MAX_X || y<0 || y>MAX_Y || dist[y][x]) continue;
+ dist[y][x] = d;
+
+ xq.enqueue (x);
+ xq.enqueue (x);
+ xq.enqueue (x-1);
+ xq.enqueue (x+1);
+
+ yq.enqueue (y-1);
+ yq.enqueue (y+1);
+ yq.enqueue (y);
+ yq.enqueue (y);
+
+ dq.enqueue (d+1);
+ dq.enqueue (d+1);
+ dq.enqueue (d+1);
+ dq.enqueue (d+1);
+ }
+}
+
+Move *
+PathFinder::search (Map *_map, int _x, int _y) {
+ int xpos=_map->xpos ();
+ int ypos=_map->ypos ();
+ if (xpos == _x && ypos == _y) return 0;
+
+ for (int y=0; y<=MAX_Y; y++) {
+ for (int x=0; x<=MAX_X; x++) {
+ if (_map->empty (x, y)) dist[y][x] = 0;
+ else dist[y][x] = PATH_WALL;
+ }
+ }
+
+ BFS (_x, _y);
+
+#if 0
+ for (int y=0; y<=MAX_Y; y++) {
+ for (int x=0; x<=MAX_X; x++) {
+ //if (x==_x && y==_y) {printf ("++ "); continue;}
+ //if (x==xpos && y==ypos) {printf ("@@ "); continue;}
+ if (dist[y][x] == PATH_WALL) {printf ("## "); continue;}
+ printf ("%02d ", dist[y][x]);
+ }
+ printf ("\n");
+ }
+#endif
+
+ int d;
+ Move *move=new Move (xpos, ypos);
+ int oldX, oldY;
+ for (;;) {
+ oldX = xpos;
+ oldY = ypos;
+
+ if (xpos == _x && ypos == _y) {
+ move->finish ();
+ //printf ("move->finish ()\n");
+ return move;
+ }
+
+ d = dist[ypos][xpos];
+
+ while (ypos-1 >= 0 && dist[ypos-1][xpos] < d) {
+ ypos--;
+ d = dist[ypos][xpos];
+ }
+ if (oldY != ypos) {
+ move->step (xpos, ypos);
+ //printf ("step (%d, %d)\n", xpos, ypos);
+ continue;
+ }
+
+ while (ypos+1 <= MAX_Y && dist[ypos+1][xpos] < d) {
+ ypos++;
+ d = dist[ypos][xpos];
+ }
+ if (oldY != ypos) {
+ move->step (xpos, ypos);
+ //printf ("step (%d, %d)\n", xpos, ypos);
+ continue;
+ }
+
+ while (xpos-1 >= 0 && dist[ypos][xpos-1] < d) {
+ xpos--;
+ d = dist[ypos][xpos];
+ }
+ if (oldX != xpos) {
+ move->step (xpos, ypos);
+ //printf ("step (%d, %d)\n", xpos, ypos);
+ continue;
+ }
+
+ while (xpos+1 <= MAX_X && dist[ypos][xpos+1] < d) {
+ xpos++;
+ d = dist[ypos][xpos];
+ }
+ if (oldX != xpos) {
+ move->step (xpos, ypos);
+ //printf ("step (%d, %d)\n", xpos, ypos);
+ continue;
+ }
+
+ delete move;
+ return 0;
+ }
+}
+
+Move*
+PathFinder::drag(int /* x1 */, int /* y1 */, int /* x2 */, int /* y2 */) {
+ return 0;
+}
+
+bool
+PathFinder::canDrag(int /* x */, int /* y */) const {
+ return false;
+}
+
+bool
+PathFinder::canWalkTo(int /* x */, int /* y */) const {
+ return false;
+}
+
+bool
+PathFinder::canDragTo(int /* x */, int /* y */) const {
+ return false;
+}
+
+void
+PathFinder::updatePossibleMoves() {
+}
+
+void
+PathFinder::updatePossibleDestinations(int /* x */, int /* y */) {
+}
+
diff --git a/ksokoban/PathFinder.h b/ksokoban/PathFinder.h
new file mode 100644
index 00000000..63187810
--- /dev/null
+++ b/ksokoban/PathFinder.h
@@ -0,0 +1,48 @@
+/*
+ * 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
+ */
+
+#ifndef PATHFINDER_H
+#define PATHFINDER_H
+
+#include "Map.h"
+class Move;
+
+#define PATH_WALL 32767
+
+class PathFinder {
+public:
+ Move *search (Map *_map, int _x, int _y);
+ Move* drag(int x1, int y1, int x2, int y2);
+ bool canDrag(int x, int y) const;
+ bool canWalkTo(int x, int y) const;
+ bool canDragTo(int x, int y) const;
+ void updatePossibleMoves();
+ void updatePossibleDestinations(int x, int y);
+
+protected:
+ //static const int PATH_WALL=32767;
+
+ int dist[MAX_Y+1][MAX_X+1];
+
+ void BFS (int _x, int _y);
+
+
+};
+
+#endif /* PATHFINDER_H */
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;
+}
diff --git a/ksokoban/PlayField.h b/ksokoban/PlayField.h
new file mode 100644
index 00000000..57524ea8
--- /dev/null
+++ b/ksokoban/PlayField.h
@@ -0,0 +1,149 @@
+/*
+ * 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
+ */
+
+#ifndef PLAYFIELD_H
+#define PLAYFIELD_H
+
+#include <qwidget.h>
+#include <qstring.h>
+#include <qfont.h>
+#include <qfontmetrics.h>
+#include <qpixmap.h>
+#include <qimage.h>
+#include <qbrush.h>
+
+#include "ImageData.h"
+#include "LevelMap.h"
+class MapDelta;
+class MoveSequence;
+class Move;
+#include "PathFinder.h"
+
+class History;
+class Bookmark;
+class LevelCollection;
+class QPainter;
+class QCursor;
+
+class PlayField : public QWidget {
+ Q_OBJECT
+public:
+ PlayField(QWidget *parent, const char *name=0, WFlags f=0);
+ ~PlayField ();
+
+ bool canMoveNow();
+ int animDelay() { return animDelay_; }
+
+ void setSize(int w, int h);
+ void level(int _l) { levelMap_->level(_l); }
+ LevelCollection *collection() const { return levelMap_->collection(); }
+ void setBookmark(Bookmark *bm);
+ void goToBookmark(Bookmark *bm);
+
+ int level() const;
+ const QString &collectionName();
+ int totalMoves() const;
+ int totalPushes() const;
+
+ void updateCollectionXpm();
+ void updateTextXpm();
+ void updateLevelXpm();
+ void updateStepsXpm();
+ void updatePushesXpm();
+
+public slots:
+ void nextLevel();
+ void previousLevel();
+ void undo();
+ void redo();
+ void restartLevel();
+ void changeCollection(LevelCollection *collection);
+ void changeAnim(int num);
+
+protected:
+ ImageData *imageData_;
+ LevelMap *levelMap_;
+ History *history_;
+ int lastLevel_;
+ MoveSequence *moveSequence_;
+ MapDelta *mapDelta_;
+ bool moveInProgress_;
+ bool dragInProgress_;
+ PathFinder pathFinder_;
+ int animDelay_;
+ const QCursor* cursor_;
+
+ void levelChange ();
+ void paintSquare (int x, int y, QPainter &paint);
+ void paintDelta ();
+ void paintEvent (QPaintEvent *e);
+ void paintPainterClip(QPainter& paint, int x, int y, int w, int h);
+ void paintPainter(QPainter& paint, const QRect& rect);
+ void resizeEvent (QResizeEvent *e);
+ void mouseMoveEvent(QMouseEvent* e);
+ void keyPressEvent (QKeyEvent *);
+ void focusInEvent (QFocusEvent *);
+ void focusOutEvent (QFocusEvent *);
+ void mousePressEvent (QMouseEvent *);
+ void mouseReleaseEvent(QMouseEvent*);
+ void leaveEvent(QEvent*);
+ void wheelEvent (QWheelEvent *);
+ void step (int _x, int _y);
+ void push (int _x, int _y);
+ virtual void timerEvent (QTimerEvent *);
+ void stopDrag();
+ void dragObject(int xpixel, int ypixel);
+ void highlight();
+ void changeCursor(const QCursor* c);
+ void eatKeyPressEvents();
+
+private:
+ int size_, xOffs_, yOffs_;
+ int highlightX_, highlightY_;
+ int dragX_, dragY_;
+ int lastMouseXPos_, lastMouseYPos_;
+ int mousePosX_, mousePosY_;
+ int wheelDelta_;
+
+ int x2pixel (int x) const { return size_*x+xOffs_; }
+ int y2pixel (int y) const { return size_*y+yOffs_; }
+
+ int pixel2x (int x) const { return (x-xOffs_)/size_; }
+ int pixel2y (int y) const { return (y-yOffs_)/size_; }
+
+ void startMoving (Move *m);
+ void startMoving (MoveSequence *ms);
+ void stopMoving ();
+
+ QRect pnumRect_, ptxtRect_, snumRect_, stxtRect_, lnumRect_, ltxtRect_;
+ QRect collRect_;
+
+ const QString levelText_, stepsText_, pushesText_;
+ QPixmap pnumXpm_, ptxtXpm_, snumXpm_, stxtXpm_, lnumXpm_, ltxtXpm_;
+ QPixmap collXpm_;
+ QPixmap dragXpm_;
+ QImage dragImage_;
+ QFont statusFont_;
+ QFontMetrics statusMetrics_;
+ QBrush background_;
+ QBrush floor_;
+
+};
+
+#endif /* PLAYFIELD_H */
diff --git a/ksokoban/Queue.h b/ksokoban/Queue.h
new file mode 100644
index 00000000..cab9db90
--- /dev/null
+++ b/ksokoban/Queue.h
@@ -0,0 +1,56 @@
+/*
+ * 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
+ */
+
+#ifndef QUEUE_H
+#define QUEUE_H
+
+#include <assert.h>
+
+template <class Type, int SizePow2>
+class Queue {
+private:
+ Type *queue_;
+ long head_, tail_;
+
+public:
+ void clear() { head_ = tail_ = 0; }
+ bool empty() { return head_ == tail_; }
+ bool full() { return ((tail_ + 1) & ((1l<<SizePow2)-1)) == head_; }
+
+ Queue() {
+ queue_ = new Type[1l << SizePow2];
+ clear();
+ }
+ ~Queue() { delete [] queue_; }
+
+ void enqueue(Type _x) {
+ assert(!full());
+ queue_[tail_] = _x;
+ tail_ = (tail_ + 1) & ((1l<<SizePow2)-1);
+ }
+
+ Type dequeue() {
+ assert(!empty());
+ Type x = queue_[head_];
+ head_ = (head_ + 1) & ((1l<<SizePow2)-1);
+ return x;
+ }
+};
+
+#endif /* QUEUE_H */
diff --git a/ksokoban/README b/ksokoban/README
new file mode 100644
index 00000000..c9b4e399
--- /dev/null
+++ b/ksokoban/README
@@ -0,0 +1,33 @@
+ksokoban 0.2.2 - a Sokoban game for KDE
+
+copyright 1998 Anders Widell <d95-awi@nada.kth.se>
+
+ksokoban is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License. See the file
+COPYING for details.
+
+See http://hem.passagen.se/awl/ksokoban/ for later versions of ksokoban.
+See the file AUTHORS for details about where the levels come from.
+
+------------------------------------------------------------------------
+
+PLAYING
+=======
+
+The objective of the game is to push all the red gems (these should
+actually have been crates, but gems looked nicer) to the goal squares,
+which are marked with green glassy things on the floor.
+
+Use the cursor keys to move about. If you move onto a gem and there is
+noting blocking it on the opposite side, then you will push the gem.
+
+Use the CONTROL key together with the cursor keys to move as far as
+possible in a direction without pushing any gems. With the SHIFT key
+you will move as far as possible in a direction, possibly pushing a
+gem if it is in the way.
+
+Use the left mouse button to move to any place you can reach without
+pushing any gems. The middle mouse moves in a straight line, possibly
+pushing a gem if it is in the way.
+
+The U key or the right mouse button undoes the last move.
diff --git a/ksokoban/StaticImage.cpp b/ksokoban/StaticImage.cpp
new file mode 100644
index 00000000..49a2aafa
--- /dev/null
+++ b/ksokoban/StaticImage.cpp
@@ -0,0 +1,86 @@
+/*
+ * 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 "config.h"
+
+#include <assert.h>
+#include "StaticImage.h"
+
+#include "images/data.c"
+
+const unsigned char *const
+imageData[NO_OF_IMAGES] = {
+ halfstone_1_data_,
+ halfstone_2_data_,
+ halfstone_3_data_,
+ halfstone_4_data_,
+
+ stone_1_data_,
+ stone_2_data_,
+ stone_3_data_,
+ stone_4_data_,
+ stone_5_data_,
+ stone_6_data_,
+
+ object_data_,
+ treasure_data_,
+ goal_data_,
+ man_data_,
+ saveman_data_,
+};
+
+const unsigned
+imageSize[NO_OF_IMAGES] = {
+ sizeof halfstone_1_data_,
+ sizeof halfstone_2_data_,
+ sizeof halfstone_3_data_,
+ sizeof halfstone_4_data_,
+
+ sizeof stone_1_data_,
+ sizeof stone_2_data_,
+ sizeof stone_3_data_,
+ sizeof stone_4_data_,
+ sizeof stone_5_data_,
+ sizeof stone_6_data_,
+
+ sizeof object_data_,
+ sizeof treasure_data_,
+ sizeof goal_data_,
+ sizeof man_data_,
+ sizeof saveman_data_,
+};
+
+StaticImage::StaticImage () {
+ bool valid = background_.loadFromData((const uchar *) starfield_data_,
+ (uint) sizeof (starfield_data_));
+
+ if (!valid) {
+ background_.resize(128, 128);
+ background_.fill(Qt::black);
+ }
+
+ for (int i=0; i<NO_OF_IMAGES; i++) {
+ images_[i].loadFromData((const uchar *) imageData[i], (uint) imageSize[i]);
+ }
+
+ //resize(32,32);
+}
+
+StaticImage::~StaticImage () {
+}
diff --git a/ksokoban/StaticImage.h b/ksokoban/StaticImage.h
new file mode 100644
index 00000000..91f76aae
--- /dev/null
+++ b/ksokoban/StaticImage.h
@@ -0,0 +1,33 @@
+/*
+ * 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
+ */
+
+#ifndef STATICIMAGE_H
+#define STATICIMAGE_H
+
+#include "ImageData.h"
+
+class
+StaticImage : public ImageData {
+public:
+ StaticImage ();
+ virtual ~StaticImage ();
+
+};
+
+#endif /* STATICIMAGE_H */
diff --git a/ksokoban/TODO b/ksokoban/TODO
new file mode 100644
index 00000000..4eed7abd
--- /dev/null
+++ b/ksokoban/TODO
@@ -0,0 +1,5 @@
+* Update the documentation
+* Go to specific level
+* Level editor
+* Drag & drop movement
+* Scores
diff --git a/ksokoban/data/Makefile.am b/ksokoban/data/Makefile.am
new file mode 100644
index 00000000..67f36885
--- /dev/null
+++ b/ksokoban/data/Makefile.am
@@ -0,0 +1,5 @@
+
+KDE_ICON = ksokoban
+
+xdg_apps_DATA = ksokoban.desktop
+
diff --git a/ksokoban/data/hi128-app-ksokoban.png b/ksokoban/data/hi128-app-ksokoban.png
new file mode 100644
index 00000000..25ff5810
--- /dev/null
+++ b/ksokoban/data/hi128-app-ksokoban.png
Binary files differ
diff --git a/ksokoban/data/hi16-app-ksokoban.png b/ksokoban/data/hi16-app-ksokoban.png
new file mode 100644
index 00000000..a6f1b41b
--- /dev/null
+++ b/ksokoban/data/hi16-app-ksokoban.png
Binary files differ
diff --git a/ksokoban/data/hi22-app-ksokoban.png b/ksokoban/data/hi22-app-ksokoban.png
new file mode 100644
index 00000000..3239ddbf
--- /dev/null
+++ b/ksokoban/data/hi22-app-ksokoban.png
Binary files differ
diff --git a/ksokoban/data/hi32-app-ksokoban.png b/ksokoban/data/hi32-app-ksokoban.png
new file mode 100644
index 00000000..f9050856
--- /dev/null
+++ b/ksokoban/data/hi32-app-ksokoban.png
Binary files differ
diff --git a/ksokoban/data/hi48-app-ksokoban.png b/ksokoban/data/hi48-app-ksokoban.png
new file mode 100644
index 00000000..9de2975d
--- /dev/null
+++ b/ksokoban/data/hi48-app-ksokoban.png
Binary files differ
diff --git a/ksokoban/data/hi64-app-ksokoban.png b/ksokoban/data/hi64-app-ksokoban.png
new file mode 100644
index 00000000..1ddce158
--- /dev/null
+++ b/ksokoban/data/hi64-app-ksokoban.png
Binary files differ
diff --git a/ksokoban/data/ksokoban.desktop b/ksokoban/data/ksokoban.desktop
new file mode 100644
index 00000000..eed35f4c
--- /dev/null
+++ b/ksokoban/data/ksokoban.desktop
@@ -0,0 +1,70 @@
+[Desktop Entry]
+Name=KSokoban
+Name[af]=Ksokoban
+Name[be]=Сакабан
+Name[bn]=কে-সোকোবান
+Name[hi]=के-शोकोबॉन
+Name[mk]=КСокобан
+Name[nb]=Sokoban
+Name[ne]=केडीई सोकोबान
+Name[pl]=Sokoban
+Name[sv]=Ksokoban
+Name[ta]=Kசோகோபான்
+Name[tg]=KСокобан
+Name[zh_TW]=KSokoban 倉庫番
+Name[zu]=I-KSokoban
+GenericName=Sokoban Game
+GenericName[be]=Гульня Сакабан
+GenericName[bg]=Логическа игра
+GenericName[bn]=সোকোবান খেলা
+GenericName[bs]=Igra Sokoban
+GenericName[ca]=Joc Sokoban
+GenericName[cs]=Hra Sokoban
+GenericName[cy]=Gêm Sokoban
+GenericName[da]=Sokoban spil
+GenericName[de]=Sokoban Spiel
+GenericName[el]=Παιχνίδι Sokoban
+GenericName[eo]=Sokobana ludo
+GenericName[es]=Juego Sokoban
+GenericName[et]=Sokoban
+GenericName[eu]=Sokoban jokoa
+GenericName[fa]=بازی Sokoban
+GenericName[fi]=Sokoban
+GenericName[fr]=Jeu Sokoban
+GenericName[ga]=Cluiche Sokoban
+GenericName[he]=משחק Sokoban
+GenericName[hr]=Sokoban
+GenericName[hu]=Sokoban
+GenericName[is]=Sokoban leikur
+GenericName[it]=Gioco del Sokoban
+GenericName[ja]=倉庫番ゲーム
+GenericName[km]=ល្បែង​សូកូបាន
+GenericName[lt]=Sokoban žaidimas
+GenericName[lv]=Sokoban spēle
+GenericName[mk]=Игра Сокобан
+GenericName[nb]=Sokoban-spill
+GenericName[nds]=Sokoban-Speel
+GenericName[ne]=सोकोबान खेल
+GenericName[nl]=Sokobanspel
+GenericName[nn]=Sokoban-spel
+GenericName[pl]=Sokoban
+GenericName[pt]=Jogo de Sokoban
+GenericName[pt_BR]=Jogo de Sokoban
+GenericName[ru]=Сокобан
+GenericName[se]=Sokoban-speallu
+GenericName[sk]=Hra Sokoban
+GenericName[sl]=Igra Sokobana
+GenericName[sr]=Игра Sokoban-а
+GenericName[sr@Latn]=Igra Sokoban-a
+GenericName[sv]=Sokoban-spel
+GenericName[ta]=சொகோபான் விளையாட்டு
+GenericName[uk]=Гра Sokoban
+GenericName[zh_TW]=倉庫番遊戲
+DocPath=ksokoban/index.html
+Exec=ksokoban %i %m -caption "%c"
+Icon=ksokoban
+Terminal=false
+Type=Application
+X-KDE-StartupNotify=true
+X-DCOP-ServiceType=Multi
+Categories=Qt;KDE;Game;StrategyGame;
diff --git a/ksokoban/images/Makefile.am b/ksokoban/images/Makefile.am
new file mode 100644
index 00000000..f2af8b9f
--- /dev/null
+++ b/ksokoban/images/Makefile.am
@@ -0,0 +1,88 @@
+
+noinst_DATA = data.c
+bin2c_SOURCES = bin2c.c
+bin2c_LDFLAGS = $(all_libraries)
+bin2c_LDADD = $(LIBZ)
+
+noinst_PROGRAMS = bin2c
+
+test:
+ x-povray +W200 +H200 +I$@.pov +O$@.png +p +d
+ rm -f $@.png
+
+POVFILES=floor_common.inc goal.pov halfstone_1.pov halfstone_2.pov halfstone_3.pov halfstone_4.pov man.pov man_common.inc object.pov saveman.pov stone_1.pov stone_2.pov stone_3.pov stone_4.pov stone_5.pov stone_6.pov stone_common.inc treasure.pov
+
+IMAGES=goal.png halfstone_1.png halfstone_2.png halfstone_3.png halfstone_4.png man.png object.png saveman.png stone_1.png stone_2.png stone_3.png stone_4.png stone_5.png stone_6.png treasure.png starfield.png
+
+EXTRA_DIST = $(POVFILES) $(IMAGES)
+CLEANFILES = data.c
+
+RESOLUTION=+W96 +H96
+STONE_RESOLUTION=+W96 +H48
+HALFSTONE_RESOLUTION=+W48 +H48
+
+# no antialias
+#ANTIALIAS=
+
+# normal antialias
+#ANTIALIAS=+A
+
+# slow antialias
+ANTIALIAS=+A0 +R9
+
+POVRAY=povray $(ANTIALIAS)
+
+data.c: $(IMAGES) bin2c
+ list=""; for i in $(IMAGES); do list="$$list $(srcdir)/$$i"; done; \
+ ./bin2c "" $$list
+
+############################################################################
+# Povray rules to generate images
+#
+#halfstone_1.png: halfstone_1.pov stone_common.inc
+# $(POVRAY) $(HALFSTONE_RESOLUTION) +I$< +O$@
+#
+#halfstone_2.png: halfstone_2.pov stone_common.inc
+# $(POVRAY) $(HALFSTONE_RESOLUTION) +I$< +O$@
+#
+#halfstone_3.png: halfstone_3.pov stone_common.inc
+# $(POVRAY) $(HALFSTONE_RESOLUTION) +I$< +O$@
+#
+#halfstone_4.png: halfstone_4.pov stone_common.inc
+# $(POVRAY) $(HALFSTONE_RESOLUTION) +I$< +O$@
+#
+#
+#stone_1.png: stone_1.pov stone_common.inc
+# $(POVRAY) $(STONE_RESOLUTION) +I$< +O$@
+#
+#stone_2.png: stone_2.pov stone_common.inc
+# $(POVRAY) $(STONE_RESOLUTION) +I$< +O$@
+#
+#stone_3.png: stone_3.pov stone_common.inc
+# $(POVRAY) $(STONE_RESOLUTION) +I$< +O$@
+#
+#stone_4.png: stone_4.pov stone_common.inc
+# $(POVRAY) $(STONE_RESOLUTION) +I$< +O$@
+#
+#stone_5.png: stone_5.pov stone_common.inc
+# $(POVRAY) $(STONE_RESOLUTION) +I$< +O$@
+#
+#stone_6.png: stone_6.pov stone_common.inc
+# $(POVRAY) $(STONE_RESOLUTION) +I$< +O$@
+#
+#
+#treasure.png: treasure.pov goal.pov floor_common.inc
+# $(POVRAY) $(RESOLUTION) +I$< +O$@
+#
+#object.png: object.pov floor_common.inc
+# $(POVRAY) $(RESOLUTION) +I$< +O$@
+#
+#man.png: man.pov man_common.inc floor_common.inc
+# $(POVRAY) $(RESOLUTION) +I$< +O$@
+#
+#saveman.png: saveman.pov man_common.inc goal.pov floor_common.inc
+# $(POVRAY) $(RESOLUTION) +I$< +O$@
+#
+#goal.png: goal.pov floor_common.inc
+# $(POVRAY) $(RESOLUTION) +I$< +O$@
+
diff --git a/ksokoban/images/bin2c.c b/ksokoban/images/bin2c.c
new file mode 100644
index 00000000..0dad91d1
--- /dev/null
+++ b/ksokoban/images/bin2c.c
@@ -0,0 +1,256 @@
+/*
+ * bin2c - compresses data files & converts the result to C source code
+ * Copyright (C) 1998-2000 Anders Widell <awl@hem.passagen.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
+ */
+
+/*
+ * This command uses the zlib library to compress each file given on
+ * the command line, and outputs the compressed data as C source code
+ * to the file 'data.c' in the current directory
+ *
+ */
+
+#include "../../config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef USE_LIBZ
+#include <zlib.h>
+#else
+typedef unsigned char Bytef;
+typedef unsigned long uLongf;
+#endif
+
+#define BUFSIZE 16384 /* Increase buffer size by this amount */
+
+static Bytef *source=NULL; /* Buffer containing uncompressed data */
+static Bytef *dest=NULL; /* Buffer containing compressed data */
+static uLongf sourceBufSize=0; /* Buffer size */
+#ifdef USE_LIBZ
+static uLongf destBufSize=0; /* Buffer size */
+#endif
+
+static uLongf sourceLen; /* Length of uncompressed data */
+static uLongf destLen; /* Length of compressed data */
+
+static FILE *infile=NULL; /* The input file containing binary data */
+static FILE *outfile=NULL; /* The output file 'data.c' */
+
+static const char *programName="";
+
+/*
+ * Print error message and free allocated resources
+ *
+ */
+
+static int
+error (msg1, msg2, msg3)
+ char *msg1;
+ char *msg2;
+ char *msg3;
+{
+ fprintf (stderr, "%s: %s%s%s\n", programName, msg1, msg2, msg3);
+
+ if (infile != NULL) fclose (infile);
+ if (outfile != NULL) fclose (outfile);
+ remove ("data.c");
+ free (dest);
+ free (source);
+
+ return 1;
+}
+
+/*
+ * Replacement for strrchr in case it isn't present in libc
+ *
+ */
+
+static char *
+my_strrchr (s, c)
+ char *s;
+ int c;
+{
+ char *ptr = NULL;
+
+ while (*s) {
+ if (*s == c) ptr = s;
+ s++;
+ }
+
+ return ptr;
+}
+
+#ifdef USE_LIBZ
+/*
+ * NOTE: my_compress2 is taken directly from zlib 1.1.3
+ *
+ * This is for compatibility with early versions of zlib that
+ * don't have the compress2 function.
+ *
+ */
+
+/* ===========================================================================
+ Compresses the source buffer into the destination buffer. The level
+ parameter has the same meaning as in deflateInit. sourceLen is the byte
+ length of the source buffer. Upon entry, destLen is the total size of the
+ destination buffer, which must be at least 0.1% larger than sourceLen plus
+ 12 bytes. Upon exit, destLen is the actual size of the compressed buffer.
+
+ compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
+ memory, Z_BUF_ERROR if there was not enough room in the output buffer,
+ Z_STREAM_ERROR if the level parameter is invalid.
+*/
+int my_compress2 (dest, destLen, source, sourceLen, level)
+ Bytef *dest;
+ uLongf *destLen;
+ const Bytef *source;
+ uLong sourceLen;
+ int level;
+{
+ z_stream stream;
+ int err;
+
+ stream.next_in = (Bytef*)source;
+ stream.avail_in = (uInt)sourceLen;
+#ifdef MAXSEG_64K
+ /* Check for source > 64K on 16-bit machine: */
+ if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR;
+#endif
+ stream.next_out = dest;
+ stream.avail_out = (uInt)*destLen;
+ if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR;
+
+ stream.zalloc = (alloc_func)0;
+ stream.zfree = (free_func)0;
+ stream.opaque = (voidpf)0;
+
+ err = deflateInit(&stream, level);
+ if (err != Z_OK) return err;
+
+ err = deflate(&stream, Z_FINISH);
+ if (err != Z_STREAM_END) {
+ deflateEnd(&stream);
+ return err == Z_OK ? Z_BUF_ERROR : err;
+ }
+ *destLen = stream.total_out;
+
+ err = deflateEnd(&stream);
+ return err;
+}
+#endif
+
+int
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int i;
+ const char *suffix;
+#ifdef USE_LIBZ
+ int result;
+#endif
+ unsigned j;
+ char *ptr;
+ int position;
+
+ programName = argv[0];
+
+ outfile = fopen ("data.c", "w");
+ if (outfile == NULL) {
+ fprintf (stderr, "%s: can't open 'data.c' for writing\n", argv[0]);
+ return 1;
+ }
+
+ suffix = argv[1];
+ /* Process each file given on command line */
+ for (i=2; i<argc; i++) {
+ infile = fopen (argv[i], "rb");
+ if (infile == NULL) return error ("can't open '", argv[i], "' for reading");
+
+ /* Read infile to source buffer */
+ sourceLen = 0;
+ while (!feof (infile)) {
+ if (sourceLen + BUFSIZE > sourceBufSize) {
+ sourceBufSize += BUFSIZE;
+ source = realloc (source, sourceBufSize);
+ if (source == NULL) return error ("memory exhausted", "", "");
+ }
+ sourceLen += fread (source+sourceLen, 1, BUFSIZE, infile);
+ if (ferror (infile)) return error ("error reading '", argv[i], "'");
+ }
+ fclose (infile);
+
+#ifdef USE_LIBZ
+
+ /* (Re)allocate dest buffer */
+ destLen = sourceBufSize + (sourceBufSize+9)/10 + 12;
+ if (destBufSize < destLen) {
+ destBufSize = destLen;
+ dest = realloc (dest, destBufSize);
+ if (dest == NULL) return error ("memory exhausted", "", "");
+ }
+
+ /* Compress dest buffer */
+ destLen = destBufSize;
+ result = my_compress2 (dest, &destLen, source, sourceLen, 9);
+ if (result != Z_OK) return error ("error compressing '", argv[i], "'");
+
+#else
+
+ destLen = sourceLen;
+ dest = source;
+
+#endif
+
+ /* Output dest buffer as C source code to outfile */
+ ptr = my_strrchr (argv[i], '.');
+ if (ptr != NULL) *ptr = '\0';
+ /* use only the file 2name and throw away the path name */
+ position = strlen(argv[i]) - 1;
+ while (position && argv[i][position] != '/') position--;
+ if (argv[i][position] == '/') position++;
+
+ fprintf (outfile, "static const unsigned char %s_data_%s[] = {\n", argv[i] + position, suffix);
+
+ for (j=0; j<destLen-1; j++) {
+ switch (j%8) {
+ case 0:
+ fprintf (outfile, " 0x%02x, ", ((unsigned) dest[j]) & 0xffu);
+ break;
+ case 7:
+ fprintf (outfile, "0x%02x,\n", ((unsigned) dest[j]) & 0xffu);
+ break;
+ default:
+ fprintf (outfile, "0x%02x, ", ((unsigned) dest[j]) & 0xffu);
+ break;
+ }
+ }
+
+ if ((destLen-1)%8 == 0) fprintf (outfile, " 0x%02x\n};\n\n", ((unsigned) dest[destLen-1]) & 0xffu);
+ else fprintf (outfile, "0x%02x\n};\n\n", ((unsigned) dest[destLen-1]) & 0xffu);
+ }
+
+ fclose (outfile);
+#ifdef USE_LIBZ
+ free (dest);
+#endif
+ free (source);
+
+ return 0;
+}
diff --git a/ksokoban/images/floor_common.inc b/ksokoban/images/floor_common.inc
new file mode 100644
index 00000000..a669fd97
--- /dev/null
+++ b/ksokoban/images/floor_common.inc
@@ -0,0 +1,19 @@
+// Pov-ray image source for ksokoban
+// copyright (c) 1998-1999 Anders Widell <awl@hem.passagen.se>
+
+#declare FloorColour = <114/255, 114/255, 114/255>;
+
+camera {
+ orthographic
+ location <0, 0, -50>
+ look_at <0, 0, 0>
+ right <1, 0, 0>
+ up <0, 1, 0>
+}
+
+light_source { <-5000, 10000, -10000> color rgb <1, 1, 1> }
+light_source { <-5000, 10000, -10000> color rgb <1, 1, 1> }
+
+plane { -z, -2
+ pigment { color rgb FloorColour }
+}
diff --git a/ksokoban/images/goal.png b/ksokoban/images/goal.png
new file mode 100644
index 00000000..fcac3f41
--- /dev/null
+++ b/ksokoban/images/goal.png
Binary files differ
diff --git a/ksokoban/images/goal.pov b/ksokoban/images/goal.pov
new file mode 100644
index 00000000..ba2fa4a1
--- /dev/null
+++ b/ksokoban/images/goal.pov
@@ -0,0 +1,34 @@
+// Pov-ray image source for ksokoban
+// copyright (c) 1998-1999 Anders Widell <awl@hem.passagen.se>
+
+#include "floor_common.inc"
+
+torus {
+ 0.35, 0.05
+ rotate <90, 0, 0>
+ translate <0,0,2>
+ pigment { color rgb FloorColour }
+}
+
+cylinder {< 0.35,0,2>, < 10,0,2>, 0.05 pigment { color rgb FloorColour}}
+cylinder {<-0.35,0,2>, <-10,0,2>, 0.05 pigment { color rgb FloorColour}}
+cylinder {<0, 0.35,2>, <0, 10,2>, 0.05 pigment { color rgb FloorColour}}
+cylinder {<0,-0.35,2>, <0,-10,2>, 0.05 pigment { color rgb FloorColour}}
+
+sphere {
+ 2*z, 0.3
+
+ finish {
+ ambient 0.1
+ diffuse 0.3
+ reflection .25
+ specular 1
+ roughness 0.02
+ }
+ interior {
+ ior 2.4
+ }
+ pigment { color rgbf <0.1, 1, 0.2, 0.8>}
+
+ scale <1,1,0.1>
+}
diff --git a/ksokoban/images/halfstone_1.png b/ksokoban/images/halfstone_1.png
new file mode 100644
index 00000000..267b3e45
--- /dev/null
+++ b/ksokoban/images/halfstone_1.png
Binary files differ
diff --git a/ksokoban/images/halfstone_1.pov b/ksokoban/images/halfstone_1.pov
new file mode 100644
index 00000000..e015b666
--- /dev/null
+++ b/ksokoban/images/halfstone_1.pov
@@ -0,0 +1,17 @@
+// Pov-ray image source for ksokoban
+// copyright (c) 1998-1999 Anders Widell <awl@hem.passagen.se>
+
+#include "stone_common.inc"
+
+camera {
+ orthographic
+ location <0, 0, -50>
+ look_at <0, 0, 0>
+ right <1/2, 0, 0>
+ up <0, 1/2, 0>
+}
+
+object {
+ HalfStone
+ texture { StoneTexture translate <0,1,1> }
+}
diff --git a/ksokoban/images/halfstone_2.png b/ksokoban/images/halfstone_2.png
new file mode 100644
index 00000000..96144c60
--- /dev/null
+++ b/ksokoban/images/halfstone_2.png
Binary files differ
diff --git a/ksokoban/images/halfstone_2.pov b/ksokoban/images/halfstone_2.pov
new file mode 100644
index 00000000..afc9fda1
--- /dev/null
+++ b/ksokoban/images/halfstone_2.pov
@@ -0,0 +1,17 @@
+// Pov-ray image source for ksokoban
+// copyright (c) 1998-1999 Anders Widell <awl@hem.passagen.se>
+
+#include "stone_common.inc"
+
+camera {
+ orthographic
+ location <0, 0, -50>
+ look_at <0, 0, 0>
+ right <1/2, 0, 0>
+ up <0, 1/2, 0>
+}
+
+object {
+ HalfStone
+ texture { StoneTexture translate <0,2,0> }
+}
diff --git a/ksokoban/images/halfstone_3.png b/ksokoban/images/halfstone_3.png
new file mode 100644
index 00000000..30994f96
--- /dev/null
+++ b/ksokoban/images/halfstone_3.png
Binary files differ
diff --git a/ksokoban/images/halfstone_3.pov b/ksokoban/images/halfstone_3.pov
new file mode 100644
index 00000000..d6a53bba
--- /dev/null
+++ b/ksokoban/images/halfstone_3.pov
@@ -0,0 +1,17 @@
+// Pov-ray image source for ksokoban
+// copyright (c) 1998-1999 Anders Widell <awl@hem.passagen.se>
+
+#include "stone_common.inc"
+
+camera {
+ orthographic
+ location <0, 0, -50>
+ look_at <0, 0, 0>
+ right <1/2, 0, 0>
+ up <0, 1/2, 0>
+}
+
+object {
+ HalfStone
+ texture { StoneTexture translate <0,3,0> }
+}
diff --git a/ksokoban/images/halfstone_4.png b/ksokoban/images/halfstone_4.png
new file mode 100644
index 00000000..d8137006
--- /dev/null
+++ b/ksokoban/images/halfstone_4.png
Binary files differ
diff --git a/ksokoban/images/halfstone_4.pov b/ksokoban/images/halfstone_4.pov
new file mode 100644
index 00000000..4bb286e6
--- /dev/null
+++ b/ksokoban/images/halfstone_4.pov
@@ -0,0 +1,17 @@
+// Pov-ray image source for ksokoban
+// copyright (c) 1998-1999 Anders Widell <awl@hem.passagen.se>
+
+#include "stone_common.inc"
+
+camera {
+ orthographic
+ location <0, 0, -50>
+ look_at <0, 0, 0>
+ right <1/2, 0, 0>
+ up <0, 1/2, 0>
+}
+
+object {
+ HalfStone
+ texture { StoneTexture translate <0,4,0> }
+}
diff --git a/ksokoban/images/man.png b/ksokoban/images/man.png
new file mode 100644
index 00000000..491e7ebf
--- /dev/null
+++ b/ksokoban/images/man.png
Binary files differ
diff --git a/ksokoban/images/man.pov b/ksokoban/images/man.pov
new file mode 100644
index 00000000..f5d71061
--- /dev/null
+++ b/ksokoban/images/man.pov
@@ -0,0 +1,5 @@
+// Pov-ray image source for ksokoban
+// copyright (c) 1998-1999 Anders Widell <awl@hem.passagen.se>
+
+#include "floor_common.inc"
+#include "man_common.inc"
diff --git a/ksokoban/images/man_common.inc b/ksokoban/images/man_common.inc
new file mode 100644
index 00000000..87fa7423
--- /dev/null
+++ b/ksokoban/images/man_common.inc
@@ -0,0 +1,59 @@
+// Pov-ray image source for ksokoban
+// copyright (c) 1998-1999 Anders Widell <awl@hem.passagen.se>
+
+#include "colors.inc"
+
+blob {
+ threshold .5
+
+ sphere { <0, .4, 0>, .2, 1 pigment {Flesh} } // head
+
+
+ sphere { <-.04, .42, -.1>, .025, -1 pigment {Flesh} } // left eye hole
+ sphere { < .04, .42, -.1>, .025, -1 pigment {Flesh} } // right eye hole
+
+ //sphere { <-.04, .42, -.09>, .025, 1 pigment {Flesh} } // left eye
+ //sphere { < .04, .42, -.09>, .025, 1 pigment {Flesh} } // right eye
+
+
+
+ cylinder { <0, .415, -.1>, <0, .4, -.11>, .02, 1 pigment {Flesh} } // nose
+
+
+ cylinder { <0, .4, 0>, <0, .2, 0>, .075, 1 pigment {Flesh} } // neck
+ sphere { <0, .4, 0>, .05, -1 pigment {Flesh} } // head-
+ sphere { <0, .2, 0>, .05, -1 pigment {Flesh} } // shoulder-
+
+ cylinder { <-.2,.2,0>, <.2,.2,0>, .115, .9 pigment {White} } // shoulder
+ cylinder { <-.1,.1,0>, <.1,.1,0>, .115, .9 pigment {White} } // stomach
+ cylinder { <-.1, 0,0>, <.1, 0,0>, .115, .9 pigment {White} } // stomach
+ cylinder { <-.1,-.05,0>, <.1,-.05,0>, .115, 1 pigment {White} } // stomach
+ cylinder { <-.1,-.05,0>, <.1,-.05,0>, .115, -1 pigment {White} } // stomach
+ cylinder { <-.1,-.1,0>, <.1,-.1,0>, .115, .9 pigment {Blue} } // waist
+
+
+// Left arm
+
+ cylinder { <-.2,.2,0>, <-.3, 0,0>, .1, 1 pigment {White } }
+ sphere { <-.2,.2,0>, .1, -1 pigment {White } } // shoulder-
+ cylinder { <-.3,0,0>, <-.2,-.1,-.1>, .075, 1 pigment {Flesh } }
+ sphere { <-.3,0,0>, .075, -1 pigment {Flesh } } // arm-
+
+
+// Right arm
+
+ cylinder { < .2,.2,0>, < .3, 0,0>, .1, 1 pigment {White}}
+ sphere { < .2,.2,0>, .1, -1 pigment {White}} // shoulder-
+ cylinder { < .3,0,0>, < .2,-.1,-.1>, .075, 1 pigment {Flesh}}
+ sphere { < .3,0,0>, .075, -1 pigment {Flesh}} // arm-
+
+ // left leg
+ cylinder { <-.1,-.2,0>, <-.125,-.5,-.025>, .15, 1 pigment {Blue } }
+
+ // right leg
+ cylinder { < .1,-.2,0>, < .125,-.5,-.025>, .15, 1 pigment {Blue } }
+
+
+ scale 0.95
+ translate 0.01*y
+}
diff --git a/ksokoban/images/object.png b/ksokoban/images/object.png
new file mode 100644
index 00000000..f6a842d0
--- /dev/null
+++ b/ksokoban/images/object.png
Binary files differ
diff --git a/ksokoban/images/object.pov b/ksokoban/images/object.pov
new file mode 100644
index 00000000..813af77f
--- /dev/null
+++ b/ksokoban/images/object.pov
@@ -0,0 +1,36 @@
+// Pov-ray image source for ksokoban
+// copyright (c) 1998-1999 Anders Widell <awl@hem.passagen.se>
+
+#include "floor_common.inc"
+
+object {
+ intersection {
+ plane {-z, 0.3 rotate < 30, 0, 0>}
+ plane {-z, 0.3 rotate < 30, 60, 0>}
+ plane {-z, 0.3 rotate < 30, 120, 0>}
+ plane {-z, 0.3 rotate < 30, 180, 0>}
+ plane {-z, 0.3 rotate < 30, 240, 0>}
+ plane {-z, 0.3 rotate < 30, 300, 0>}
+
+ plane {-z, 0.3 rotate <-50, 0, 0>}
+ plane {-z, 0.3 rotate <-50, 60, 0>}
+ plane {-z, 0.3 rotate <-50, 120, 0>}
+ plane {-z, 0.3 rotate <-50, 180, 0>}
+ plane {-z, 0.3 rotate <-50, 240, 0>}
+ plane {-z, 0.3 rotate <-50, 300, 0>}
+ }
+
+ finish {
+ ambient 0.1
+ diffuse 0.3
+ reflection .25
+ specular 1
+ roughness 0.02
+ }
+ interior {
+ ior 2.4
+ }
+ pigment { color rgbf <1, 0.1, 0.2, 0.8>}
+
+ translate <0, -0.1, 0>
+}
diff --git a/ksokoban/images/saveman.png b/ksokoban/images/saveman.png
new file mode 100644
index 00000000..c859047c
--- /dev/null
+++ b/ksokoban/images/saveman.png
Binary files differ
diff --git a/ksokoban/images/saveman.pov b/ksokoban/images/saveman.pov
new file mode 100644
index 00000000..df6ad750
--- /dev/null
+++ b/ksokoban/images/saveman.pov
@@ -0,0 +1,5 @@
+// Pov-ray image source for ksokoban
+// copyright (c) 1998-1999 Anders Widell <awl@hem.passagen.se>
+
+#include "goal.pov"
+#include "man_common.inc"
diff --git a/ksokoban/images/starfield.png b/ksokoban/images/starfield.png
new file mode 100644
index 00000000..e09204be
--- /dev/null
+++ b/ksokoban/images/starfield.png
Binary files differ
diff --git a/ksokoban/images/stone_1.png b/ksokoban/images/stone_1.png
new file mode 100644
index 00000000..221975ca
--- /dev/null
+++ b/ksokoban/images/stone_1.png
Binary files differ
diff --git a/ksokoban/images/stone_1.pov b/ksokoban/images/stone_1.pov
new file mode 100644
index 00000000..357abafb
--- /dev/null
+++ b/ksokoban/images/stone_1.pov
@@ -0,0 +1,17 @@
+// Pov-ray image source for ksokoban
+// copyright (c) 1998-1999 Anders Widell <awl@hem.passagen.se>
+
+#include "stone_common.inc"
+
+camera {
+ orthographic
+ location <0, 0, -50>
+ look_at <0, 0, 0>
+ right <1, 0, 0>
+ up <0, 1/2, 0>
+}
+
+object {
+ Stone
+ texture { StoneTexture translate <1,0,0> }
+}
diff --git a/ksokoban/images/stone_2.png b/ksokoban/images/stone_2.png
new file mode 100644
index 00000000..71ea2962
--- /dev/null
+++ b/ksokoban/images/stone_2.png
Binary files differ
diff --git a/ksokoban/images/stone_2.pov b/ksokoban/images/stone_2.pov
new file mode 100644
index 00000000..791e0f9e
--- /dev/null
+++ b/ksokoban/images/stone_2.pov
@@ -0,0 +1,17 @@
+// Pov-ray image source for ksokoban
+// copyright (c) 1998-1999 Anders Widell <awl@hem.passagen.se>
+
+#include "stone_common.inc"
+
+camera {
+ orthographic
+ location <0, 0, -50>
+ look_at <0, 0, 0>
+ right <1, 0, 0>
+ up <0, 1/2, 0>
+}
+
+object {
+ Stone
+ texture { StoneTexture translate <2,0,0> }
+}
diff --git a/ksokoban/images/stone_3.png b/ksokoban/images/stone_3.png
new file mode 100644
index 00000000..a85c5eec
--- /dev/null
+++ b/ksokoban/images/stone_3.png
Binary files differ
diff --git a/ksokoban/images/stone_3.pov b/ksokoban/images/stone_3.pov
new file mode 100644
index 00000000..8d983b5b
--- /dev/null
+++ b/ksokoban/images/stone_3.pov
@@ -0,0 +1,17 @@
+// Pov-ray image source for ksokoban
+// copyright (c) 1998-1999 Anders Widell <awl@hem.passagen.se>
+
+#include "stone_common.inc"
+
+camera {
+ orthographic
+ location <0, 0, -50>
+ look_at <0, 0, 0>
+ right <1, 0, 0>
+ up <0, 1/2, 0>
+}
+
+object {
+ Stone
+ texture { StoneTexture translate <3,0,0> }
+}
diff --git a/ksokoban/images/stone_4.png b/ksokoban/images/stone_4.png
new file mode 100644
index 00000000..5f17e094
--- /dev/null
+++ b/ksokoban/images/stone_4.png
Binary files differ
diff --git a/ksokoban/images/stone_4.pov b/ksokoban/images/stone_4.pov
new file mode 100644
index 00000000..5d3a5636
--- /dev/null
+++ b/ksokoban/images/stone_4.pov
@@ -0,0 +1,17 @@
+// Pov-ray image source for ksokoban
+// copyright (c) 1998-1999 Anders Widell <awl@hem.passagen.se>
+
+#include "stone_common.inc"
+
+camera {
+ orthographic
+ location <0, 0, -50>
+ look_at <0, 0, 0>
+ right <1, 0, 0>
+ up <0, 1/2, 0>
+}
+
+object {
+ Stone
+ texture { StoneTexture translate <4,0,0> }
+}
diff --git a/ksokoban/images/stone_5.png b/ksokoban/images/stone_5.png
new file mode 100644
index 00000000..7a29b25d
--- /dev/null
+++ b/ksokoban/images/stone_5.png
Binary files differ
diff --git a/ksokoban/images/stone_5.pov b/ksokoban/images/stone_5.pov
new file mode 100644
index 00000000..612dfffc
--- /dev/null
+++ b/ksokoban/images/stone_5.pov
@@ -0,0 +1,17 @@
+// Pov-ray image source for ksokoban
+// copyright (c) 1998-1999 Anders Widell <awl@hem.passagen.se>
+
+#include "stone_common.inc"
+
+camera {
+ orthographic
+ location <0, 0, -50>
+ look_at <0, 0, 0>
+ right <1, 0, 0>
+ up <0, 1/2, 0>
+}
+
+object {
+ Stone
+ texture { StoneTexture translate <5,0,0> }
+}
diff --git a/ksokoban/images/stone_6.png b/ksokoban/images/stone_6.png
new file mode 100644
index 00000000..22eefb05
--- /dev/null
+++ b/ksokoban/images/stone_6.png
Binary files differ
diff --git a/ksokoban/images/stone_6.pov b/ksokoban/images/stone_6.pov
new file mode 100644
index 00000000..463c5131
--- /dev/null
+++ b/ksokoban/images/stone_6.pov
@@ -0,0 +1,17 @@
+// Pov-ray image source for ksokoban
+// copyright (c) 1998-1999 Anders Widell <awl@hem.passagen.se>
+
+#include "stone_common.inc"
+
+camera {
+ orthographic
+ location <0, 0, -50>
+ look_at <0, 0, 0>
+ right <1, 0, 0>
+ up <0, 1/2, 0>
+}
+
+object {
+ Stone
+ texture { StoneTexture translate <6,0,0> }
+}
diff --git a/ksokoban/images/stone_common.inc b/ksokoban/images/stone_common.inc
new file mode 100644
index 00000000..3c23a86d
--- /dev/null
+++ b/ksokoban/images/stone_common.inc
@@ -0,0 +1,32 @@
+// Pov-ray image source for ksokoban
+// copyright (c) 1998-1999 Anders Widell <awl@hem.passagen.se>
+
+#include "colors.inc"
+#include "stones.inc"
+
+#declare StoneTexture = T_Stone8
+
+background { color rgb <0, 0, 0> }
+
+light_source {
+ <-1000000/3, 1000000/3, -1000000> color rgb <0.625, 0.625, 0.625>
+}
+light_source {
+ <-1000000/3, 1000000/3, -1000000> color rgb <0.625, 0.625, 0.625>
+}
+
+#declare Stone = object {
+ superellipsoid {
+ <1/3, 1/3>
+
+ scale <1/2, 1/4, 1/4>
+ }
+}
+
+#declare HalfStone = object {
+ superellipsoid {
+ <1/2, 1/3>
+
+ scale <1/4, 1/4, 1/4>
+ }
+}
diff --git a/ksokoban/images/treasure.png b/ksokoban/images/treasure.png
new file mode 100644
index 00000000..2560f1ef
--- /dev/null
+++ b/ksokoban/images/treasure.png
Binary files differ
diff --git a/ksokoban/images/treasure.pov b/ksokoban/images/treasure.pov
new file mode 100644
index 00000000..2f4ccfe7
--- /dev/null
+++ b/ksokoban/images/treasure.pov
@@ -0,0 +1,36 @@
+// Pov-ray image source for ksokoban
+// copyright (c) 1998-1999 Anders Widell <awl@hem.passagen.se>
+
+#include "goal.pov"
+
+object {
+ intersection {
+ plane {-z, 0.3 rotate < 30, 0, 0>}
+ plane {-z, 0.3 rotate < 30, 60, 0>}
+ plane {-z, 0.3 rotate < 30, 120, 0>}
+ plane {-z, 0.3 rotate < 30, 180, 0>}
+ plane {-z, 0.3 rotate < 30, 240, 0>}
+ plane {-z, 0.3 rotate < 30, 300, 0>}
+
+ plane {-z, 0.3 rotate <-50, 0, 0>}
+ plane {-z, 0.3 rotate <-50, 60, 0>}
+ plane {-z, 0.3 rotate <-50, 120, 0>}
+ plane {-z, 0.3 rotate <-50, 180, 0>}
+ plane {-z, 0.3 rotate <-50, 240, 0>}
+ plane {-z, 0.3 rotate <-50, 300, 0>}
+ }
+
+ finish {
+ ambient 0.1
+ diffuse 0.3
+ reflection .25
+ specular 1
+ roughness 0.02
+ }
+ interior {
+ ior 2.4
+ }
+ pigment { color rgbf <1, 0.1, 0.2, 0.8>}
+
+ translate <0, -0.1, 0>
+}
diff --git a/ksokoban/levels/Makefile.am b/ksokoban/levels/Makefile.am
new file mode 100644
index 00000000..d0ed02a2
--- /dev/null
+++ b/ksokoban/levels/Makefile.am
@@ -0,0 +1,11 @@
+
+noinst_DATA = data.c
+
+data.c: ../images/bin2c level.data
+ ../images/bin2c "" $(srcdir)/level.data
+
+../images/bin2c: $(srcdir)/../images/bin2c.c
+ (cd ../images && $(MAKE) bin2c)
+
+CLEANFILES=data.c
+EXTRA_DIST=level.data
diff --git a/ksokoban/levels/level.data b/ksokoban/levels/level.data
new file mode 100644
index 00000000..029e5c72
--- /dev/null
+++ b/ksokoban/levels/level.data
Binary files differ
diff --git a/ksokoban/main.cpp b/ksokoban/main.cpp
new file mode 100644
index 00000000..9c997613
--- /dev/null
+++ b/ksokoban/main.cpp
@@ -0,0 +1,86 @@
+/*
+ * ksokoban - a Sokoban game for KDE
+ * Copyright (C) 1998-2000 Anders Widell <awl@passagen.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 <kuniqueapplication.h>
+#include <kapplication.h>
+#include <kimageio.h>
+#include <klocale.h>
+#include <kcmdlineargs.h>
+#include <kaboutdata.h>
+
+#include "MainWindow.h"
+
+
+static const char description[] = I18N_NOOP("The japanese warehouse keeper game");
+
+static const char version[] = "0.4.2";
+
+
+static KCmdLineOptions options[] =
+{
+ { "+[file]", I18N_NOOP("Level collection file to load"), 0 },
+ KCmdLineLastOption // End of options.
+};
+
+
+int
+main (int argc, char **argv)
+{
+ KAboutData aboutData("ksokoban", I18N_NOOP("KSokoban"),
+ version, description, KAboutData::License_GPL,
+ "(c) 1998-2001 Anders Widell", 0,
+ "http://hem.passagen.se/awl/ksokoban/");
+ aboutData.addAuthor("Anders Widell", 0,
+ "awl@passagen.se",
+ "http://hem.passagen.se/awl/");
+ aboutData.addCredit("David W. Skinner",
+ I18N_NOOP("For contributing the Sokoban levels included in this game"),
+ "sasquatch@bentonrea.com",
+ "http://users.bentonrea.com/~sasquatch/");
+ KCmdLineArgs::init(argc, argv, &aboutData);
+ KCmdLineArgs::addCmdLineOptions(options);
+// KUniqueApplication::addCmdLineOptions();
+
+// if (!KUniqueApplication::start())
+// return 0;
+
+ QApplication::setColorSpec(QApplication::ManyColor);
+
+// KUniqueApplication app;
+ KApplication app;
+// KImageIO::registerFormats();
+
+ MainWindow *widget = new MainWindow();
+ app.setMainWidget(widget);
+ widget->show();
+
+ KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
+ if (args->count() > 0) {
+ widget->openURL(args->url(0));
+ }
+ args->clear();
+
+ QObject::connect(&app, SIGNAL(lastWindowClosed()), &app, SLOT(quit()));
+
+ int rc = app.exec();
+
+// delete widget;
+
+ return rc;
+}