diff options
Diffstat (limited to 'ksokoban')
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 <[email protected]> + +------------------------------------------------------------------------ +The levels were taken from: + +xsokoban 3.3c for X-windows + www: http://xsokoban.lcs.mit.edu/xsokoban.html + author: Andrew Myers <[email protected]> + +MacSokoban 3.0.3 for Macintosh + www: http://www.lysator.liu.se/~ingemar/games.html + author: Ingemar Ragnemalm <[email protected]> + +Sokoban 2.4 for Macintosh + www: http://members.aol.com/SokobanMac/ + author: Scott Lindhurst <[email protected]> + +------------------------------------------------------------------------ +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 <[email protected]> +MacTommy inventions by a guy called Tommy in Pennsylvania +Yoshio's autogenerated by Yoshio Murase <[email protected]> + see http://www.ne.jp/asahi/ai/yoshio/sokoban/main.htm +For the kids by Ross (W.R.) Brown <[email protected]> +Simple Sokoban simplified original levels + by Phil Shapiro <[email protected]> +Dimitri & Yorick by Jacques Duthen <[email protected]> + +------------------------------------------------------------------------ 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 <[email protected]> + * + * 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 <[email protected]> + * + * 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 <[email protected]> + * + * 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 <[email protected]> + * + * 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 <[email protected]> + * @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 <[email protected]> + * + * 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 <[email protected]> + * + * 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 <[email protected]> + * + * 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 <[email protected]> + * + * 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 <[email protected]> + * + * 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 <[email protected]> + * + * 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 <[email protected]> + * + * 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 <[email protected]> + * + * 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 <[email protected]> + * + * 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 <[email protected]> + * + * 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 <[email protected]> + * + * 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 <[email protected]> + * + * 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 <[email protected]> + * + * 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 <[email protected]> + * + * 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 <[email protected]> + * + * 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 <[email protected]> + * + * 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 <[email protected]> + * @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 <[email protected]> + * + * 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 <[email protected]> + * + * 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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 <[email protected]> + * + * 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 <[email protected]> + * + * 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 <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <stdio.h> +#include <assert.h> + +#include <qwidget.h> +#include <qpixmap.h> +#include <qkeycode.h> +#include <kconfig.h> +#include <kapplication.h> +#include <klocale.h> +#include <qpainter.h> +#include <kmessagebox.h> +#include <kglobalsettings.h> + +#include "PlayField.h" +#include "ModalLabel.h" +#include "LevelMap.h" +#include "Move.h" +#include "History.h" +#include "PathFinder.h" +#include "MapDelta.h" +#include "MoveSequence.h" +#include "StaticImage.h" +#include "HtmlPrinter.h" +#include "Bookmark.h" +#include "LevelCollection.h" + +#include "PlayField.moc" + +PlayField::PlayField(QWidget *parent, const char *name, WFlags f) + : QWidget(parent, name, f|WResizeNoErase), imageData_(0), lastLevel_(-1), + moveSequence_(0), moveInProgress_(false), dragInProgress_(false), + xOffs_(0), yOffs_(0), + wheelDelta_(0), + levelText_(i18n("Level:")), stepsText_(i18n("Steps:")), + pushesText_(i18n("Pushes:")), + statusFont_(KGlobalSettings::generalFont().family(), 18, QFont::Bold), statusMetrics_(statusFont_) { + + setFocusPolicy(QWidget::StrongFocus); + setFocus(); + setBackgroundMode(Qt::NoBackground); + setMouseTracking(true); + + highlightX_ = highlightY_ = 0; + + KConfig *cfg = (KApplication::kApplication())->config(); + cfg->setGroup("settings"); + + imageData_ = new StaticImage; + + animDelay_ = cfg->readNumEntry("animDelay", 2); + if (animDelay_ < 0 || animDelay_ > 3) animDelay_ = 2; + + history_ = new History; + + background_.setPixmap(imageData_->background()); + floor_ = QColor(0x66,0x66,0x66); + + levelMap_ = new LevelMap; + mapDelta_ = new MapDelta(levelMap_); + mapDelta_->end(); + + levelChange(); +} + +PlayField::~PlayField() { + KConfig *cfg = (KApplication::kApplication())->config(); + cfg->setGroup("settings"); + cfg->writeEntry("animDelay", animDelay_, true, false, false); + + delete mapDelta_; + delete history_; + delete levelMap_; + delete imageData_; +} + +void +PlayField::changeCursor(const QCursor* c) { + if (cursor_ == c) return; + + cursor_ = c; + if (c == 0) unsetCursor(); + else setCursor(*c); +} + +int +PlayField::level() const { + if (levelMap_ == 0) return 0; + return levelMap_->level(); +} + +const QString & +PlayField::collectionName() { + static QString error = "????"; + if (levelMap_ == 0) return error; + return levelMap_->collectionName(); +} + +int +PlayField::totalMoves() const { + if (levelMap_ == 0) return 0; + return levelMap_->totalMoves(); +} + +int +PlayField::totalPushes() const{ + if (levelMap_ == 0) return 0; + return levelMap_->totalPushes(); +} + +void +PlayField::levelChange() { + stopMoving(); + stopDrag(); + history_->clear(); + setSize(width(), height()); + + updateLevelXpm(); + updateStepsXpm(); + updatePushesXpm(); + highlight(); +} + +void +PlayField::paintSquare(int x, int y, QPainter &paint) { + if (levelMap_->xpos() == x && levelMap_->ypos() == y) { + if (levelMap_->goal(x, y)) + imageData_->saveman(paint, x2pixel(x), y2pixel(y)); + else + imageData_->man(paint, x2pixel(x), y2pixel(y)); + return; + } + if (levelMap_->empty(x, y)) { + if (levelMap_->floor(x, y)) { + if (levelMap_->goal(x, y)) + imageData_->goal(paint, x2pixel(x), y2pixel(y)); + else + paint.fillRect(x2pixel(x), y2pixel(y), size_, size_, floor_); + } else { + paint.fillRect(x2pixel(x), y2pixel(y), size_, size_, background_); + } + return; + } + if (levelMap_->wall(x, y)) { + imageData_->wall(paint, x2pixel(x), y2pixel(y), x+y*(MAX_X+1), + levelMap_->wallLeft(x, y), + levelMap_->wallRight(x, y)); + return; + } + + + if (levelMap_->object(x, y)) { + if (highlightX_ == x && highlightY_ == y) { + if (levelMap_->goal(x, y)) + imageData_->brightTreasure(paint, x2pixel(x), y2pixel(y)); + else + imageData_->brightObject(paint, x2pixel(x), y2pixel(y)); + } else { + if (levelMap_->goal(x, y)) + imageData_->treasure(paint, x2pixel(x), y2pixel(y)); + else + imageData_->object(paint, x2pixel(x), y2pixel(y)); + } + return; + } +} + +void +PlayField::paintDelta() { + QPainter paint(this); + + // the following line is a workaround for a bug in Qt 2.0.1 + // (and possibly earlier versions) + paint.setBrushOrigin(0, 0); + + for (int y=0; y<levelMap_->height(); y++) { + for (int x=0; x<levelMap_->width(); x++) { + if (mapDelta_->hasChanged(x, y)) paintSquare(x, y, paint); + } + } +} + + + +void +PlayField::paintEvent(QPaintEvent *e) { + QPainter paint(this); + + // the following line is a workaround for a bug in Qt 2.0.1 + // (and possibly earlier versions) + paint.setBrushOrigin(0, 0); + + paint.setClipRegion(e->region()); + paint.setClipping(true); + + paintPainter(paint, e->rect()); +} + +void +PlayField::paintPainterClip(QPainter &paint, int x, int y, int w, int h) { + QRect rect(x, y, w, h); + + paint.setClipRect(rect); + paint.setClipping(true); + paintPainter(paint, rect); +} + +void +PlayField::paintPainter(QPainter &paint, const QRect &rect) { + if (size_ <= 0) return; + int minx = pixel2x(rect.x()); + int miny = pixel2y(rect.y()); + int maxx = pixel2x(rect.x()+rect.width()-1); + int maxy = pixel2y(rect.y()+rect.height()-1); + + if (minx < 0) minx = 0; + if (miny < 0) miny = 0; + if (maxx >= levelMap_->width()) maxx = levelMap_->width()-1; + if (maxy >= levelMap_->height()) maxy = levelMap_->height()-1; + + { + int x1, x2, y1, y2; + y1 = y2pixel(miny); + if (y1 > rect.y()) paint.fillRect(rect.x(), rect.y(), rect.width(), y1-rect.y(), background_); + + int bot=rect.y()+rect.height(); + if (bot > height()-collRect_.height()) bot = height()-collRect_.height(); + + y2 = y2pixel(maxy+1); + if (y2 < bot) paint.fillRect(rect.x(), y2, rect.width(), bot-y2, background_); + + x1 = x2pixel(minx); + if (x1 > rect.x()) paint.fillRect(rect.x(), y1, x1-rect.x(), y2-y1, background_); + + x2 = x2pixel(maxx+1); + if (x2 < rect.x()+rect.width()) paint.fillRect(x2, y1, rect.x()+rect.width()-x2, y2-y1, background_); + + // paint.eraseRect + } + + for (int y=miny; y<=maxy; y++) { + for (int x=minx; x<=maxx; x++) { + paintSquare(x, y, paint); + } + } + + if (collRect_.intersects(rect)) paint.drawPixmap(collRect_.x(), collRect_.y(), collXpm_); + if (ltxtRect_.intersects(rect)) paint.drawPixmap(ltxtRect_.x(), ltxtRect_.y(), ltxtXpm_); + if (lnumRect_.intersects(rect)) paint.drawPixmap(lnumRect_.x(), lnumRect_.y(), lnumXpm_); + if (stxtRect_.intersects(rect)) paint.drawPixmap(stxtRect_.x(), stxtRect_.y(), stxtXpm_); + if (snumRect_.intersects(rect)) paint.drawPixmap(snumRect_.x(), snumRect_.y(), snumXpm_); + if (ptxtRect_.intersects(rect)) paint.drawPixmap(ptxtRect_.x(), ptxtRect_.y(), ptxtXpm_); + if (pnumRect_.intersects(rect)) paint.drawPixmap(pnumRect_.x(), pnumRect_.y(), pnumXpm_); +} + +void +PlayField::resizeEvent(QResizeEvent *e) { + setSize(e->size().width(), e->size().height()); +} + +void +PlayField::mouseMoveEvent(QMouseEvent *e) { + lastMouseXPos_ = e->x(); + lastMouseYPos_ = e->y(); + + if (!dragInProgress_) return highlight(); + + int old_x = dragX_, old_y = dragY_; + + dragX_ = lastMouseXPos_ - mousePosX_; + dragY_ = lastMouseYPos_ - mousePosY_; + + { + int x = pixel2x(dragX_ + size_/2); + int y = pixel2y(dragY_ + size_/2); + if (x >= 0 && x < levelMap_->width() && + y >= 0 && y < levelMap_->height() && + pathFinder_.canDragTo(x, y)) { + x = x2pixel(x); + y = y2pixel(y); + + if (dragX_ >= x - size_/4 && + dragX_ < x + size_/4 && + dragY_ >= y - size_/4 && + dragY_ < y + size_/4) { + dragX_ = x; + dragY_ = y; + } + } + } + + if (dragX_ == old_x && dragY_ == old_y) return; + + QRect rect(dragX_, dragY_, size_, size_); + + dragXpm_.resize(size_, size_); + + QPainter paint; + paint.begin(&dragXpm_); + paint.setBackgroundColor(backgroundColor()); + paint.setBrushOrigin(- dragX_, - dragY_); + paint.translate((double) (- dragX_), (double) (- dragY_)); + paintPainter(paint, rect); + paint.end(); + + dragImage_ = dragXpm_; + for (int yy=0; yy<size_; yy++) { + for (int xx=0; xx<size_; xx++) { + QRgb rgb1 = imageData_->objectImg().pixel(xx, yy); + int r1 = qRed(rgb1); + int g1 = qGreen(rgb1); + int b1 = qBlue(rgb1); + if (r1 != g1 || r1 != b1 || r1 == 255) { + QRgb rgb2 = dragImage_.pixel(xx, yy); + int r2 = qRed(rgb2); + int g2 = qGreen(rgb2); + int b2 = qBlue(rgb2); + r2 = (int) (0.75 * r1 + 0.25 * r2 + 0.5); + g2 = (int) (0.75 * g1 + 0.25 * g2 + 0.5); + b2 = (int) (0.75 * b1 + 0.25 * b2 + 0.5); + dragImage_.setPixel(xx, yy, qRgb(r2, g2, b2)); + } + } + } + + paint.begin(this); + + // the following line is a workaround for a bug in Qt 2.0.1 + // (and possibly earlier versions) + paint.setBrushOrigin(0, 0); + + dragXpm_.convertFromImage(dragImage_, + OrderedDither|OrderedAlphaDither| + ColorOnly|AvoidDither); + paint.drawPixmap(dragX_, dragY_, dragXpm_); + + { + int dx = dragX_ - old_x; + int dy = dragY_ - old_y; + int y2 = old_y; + if (dy > 0) { + paintPainterClip(paint, old_x, old_y, size_, dy); + // NOTE: clipping is now activated in the QPainter paint + y2 += dy; + } else if (dy < 0) { + paintPainterClip(paint, old_x, old_y+size_+dy, size_, -dy); + // NOTE: clipping is now activated in the QPainter paint + dy = -dy; + } + if (dx > 0) { + paintPainterClip(paint, old_x, y2, dx, size_-dy); + // NOTE: clipping is now activated in the QPainter paint + } else if (dx < 0) { + paintPainterClip(paint, old_x+size_+dx, y2, -dx, size_-dy); + // NOTE: clipping is now activated in the QPainter paint + } + } + paint.end(); +} + +void +PlayField::highlight() { + // FIXME: the line below should not be needed + if (size_ == 0) return; + + int x=pixel2x(lastMouseXPos_); + int y=pixel2y(lastMouseYPos_); + + if (x < 0 || y < 0 || x >= levelMap_->width() || y >= levelMap_->height()) + return; + + if (x == highlightX_ && y == highlightY_) return; + + if (pathFinder_.canDrag(x, y)) { + QPainter paint(this); + + if (highlightX_ >= 0) { + int x = highlightX_, y = highlightY_; + highlightX_ = -1; + paintSquare(x, y, paint); + } else + changeCursor(&sizeAllCursor); + + if (levelMap_->goal(x, y)) + imageData_->brightTreasure(paint, x2pixel(x), y2pixel(y)); + else + imageData_->brightObject(paint, x2pixel(x), y2pixel(y)); + highlightX_ = x; + highlightY_ = y; + } else { + if (pathFinder_.canWalkTo(x, y)) changeCursor(&crossCursor); + else changeCursor(0); + if (highlightX_ >= 0) { + QPainter paint(this); + + int x = highlightX_, y = highlightY_; + highlightX_ = -1; + + paintSquare(x, y, paint); + } + } +} + +void +PlayField::stopMoving() { + killTimers(); + delete moveSequence_; + moveSequence_ = 0; + moveInProgress_ = false; + updateStepsXpm(); + updatePushesXpm(); + + QPainter paint(this); + paint.drawPixmap(snumRect_.x(), snumRect_.y(), snumXpm_); + paint.drawPixmap(pnumRect_.x(), pnumRect_.y(), pnumXpm_); + + pathFinder_.updatePossibleMoves(); +} + + +void +PlayField::startMoving(Move *m) { + startMoving(new MoveSequence(m, levelMap_)); +} + +void +PlayField::startMoving(MoveSequence *ms) { + static const int delay[4] = {0, 15, 35, 60}; + + assert(moveSequence_ == 0 && !moveInProgress_); + moveSequence_ = ms; + moveInProgress_ = true; + if (animDelay_) startTimer(delay[animDelay_]); + timerEvent(0); +} + +void +PlayField::timerEvent(QTimerEvent *) { + assert(moveInProgress_); + if (moveSequence_ == 0) { + killTimers(); + moveInProgress_ = false; + return; + } + + bool more=false; + + mapDelta_->start(); + if (animDelay_) more = moveSequence_->next(); + else { + while (moveSequence_->next()) if (levelMap_->completed()) break; + more = true; // FIXME: clean this up + stopMoving(); + } + mapDelta_->end(); + + if (more) { + paintDelta(); + if (levelMap_->completed()) { + stopMoving(); + ModalLabel::message(i18n("Level completed"), this); + nextLevel(); + return; + } + } else stopMoving(); +} + +void +PlayField::step(int _x, int _y) { + if (!canMoveNow()) return; + + int oldX=levelMap_->xpos(); + int oldY=levelMap_->ypos(); + int x=oldX, y=oldY; + + int dx=0, dy=0; + if (_x>oldX) dx=1; + if (_x<oldX) dx=-1; + if (_y>oldY) dy=1; + if (_y<oldY) dy=-1; + + while (!(x==_x && y==_y) && levelMap_->step(x+dx, y+dy)) { + x += dx; + y += dy; + } + + if (x!=oldX || y!=oldY) { + Move *m = new Move(oldX, oldY); + m->step(x, y); + m->finish(); + history_->add(m); + m->undo(levelMap_); + + startMoving(m); + + } +} + +void +PlayField::push(int _x, int _y) { + if (!canMoveNow()) return; + + int oldX=levelMap_->xpos(); + int oldY=levelMap_->ypos(); + int x=oldX, y=oldY; + + int dx=0, dy=0; + if (_x>oldX) dx=1; + if (_x<oldX) dx=-1; + if (_y>oldY) dy=1; + if (_y<oldY) dy=-1; + + while (!(x==_x && y==_y) && levelMap_->step(x+dx, y+dy)) { + x += dx; + y += dy; + } + int objX=x, objY=y; + while (!(x==_x && y==_y) && levelMap_->push(x+dx, y+dy)) { + x += dx; + y += dy; + } + + if (x!=oldX || y!=oldY) { + Move *m = new Move(oldX, oldY); + + if (objX!=oldX || objY!=oldY) m->step(objX, objY); + + if (objX!=x || objY!=y) { + m->push(x, y); + + objX += dx; + objY += dy; + } + m->finish(); + history_->add(m); + + m->undo(levelMap_); + + startMoving(m); + } +} + +void +PlayField::keyPressEvent(QKeyEvent * e) { + int x=levelMap_->xpos(); + int y=levelMap_->ypos(); + + switch (e->key()) { + case Key_Up: + if (e->state() & ControlButton) step(x, 0); + else if (e->state() & ShiftButton) push(x, 0); + else push(x, y-1); + break; + case Key_Down: + if (e->state() & ControlButton) step(x, MAX_Y); + else if (e->state() & ShiftButton) push(x, MAX_Y); + else push(x, y+1); + break; + case Key_Left: + if (e->state() & ControlButton) step(0, y); + else if (e->state() & ShiftButton) push(0, y); + else push(x-1, y); + break; + case Key_Right: + if (e->state() & ControlButton) step(MAX_X, y); + else if (e->state() & ShiftButton) push(MAX_X, y); + else push(x+1, y); + break; + + case Key_Q: + KApplication::kApplication()->closeAllWindows(); + break; + + case Key_Backspace: + case Key_Delete: + if (e->state() & ControlButton) redo(); + else undo(); + break; + +#if 0 + case Key_X: + levelMap_->random(); + levelChange(); + repaint(false); + break; + + case Key_R: + level(levelMap_->level()); + return; + break; + case Key_N: + nextLevel(); + return; + break; + case Key_P: + previousLevel(); + return; + break; + case Key_U: + undo(); + return; + break; + case Key_I: + history_->redo(levelMap_); + repaint(false); + return; + break; + + case Key_S: + { + QString buf; + history_->save(buf); + printf("%s\n", (char *) buf); + } + return; + break; + + case Key_L: + stopMoving(); + history_->clear(); + level(levelMap_->level()); + { + char buf[4096]="r1*D1*D1*r1*@r1*D1*"; + //scanf("%s", buf); + history_->load(levelMap_, buf); + } + updateStepsXpm(); + updatePushesXpm(); + repaint(false); + return; + break; +#endif + + + case Key_Print: + HtmlPrinter::printHtml(levelMap_); + break; + + default: + e->ignore(); + return; + break; + } +} + +void +PlayField::stopDrag() { + if (!dragInProgress_) return; + + changeCursor(0); + + QPainter paint(this); + + // the following line is a workaround for a bug in Qt 2.0.1 + // (and possibly earlier versions) + paint.setBrushOrigin(0, 0); + + int x = highlightX_, y = highlightY_; + paintSquare(x, y, paint); + + paintPainterClip(paint, dragX_, dragY_, size_, size_); + // NOTE: clipping is now activated in the QPainter paint + dragInProgress_ = false; + +} + +void +PlayField::dragObject(int xpixel, int ypixel) { + int x=pixel2x(xpixel - mousePosX_ + size_/2); + int y=pixel2y(ypixel - mousePosY_ + size_/2); + + if (x == highlightX_ && y == highlightY_) return; + + printf("drag %d,%d to %d,%d\n", highlightX_, highlightY_, x, y); + pathFinder_.drag(highlightX_, highlightY_, x, y); + stopDrag(); +} + + +void +PlayField::mousePressEvent(QMouseEvent *e) { + if (!canMoveNow()) return; + + if (dragInProgress_) { + if (e->button() == LeftButton) dragObject(e->x(), e->y()); + else stopDrag(); + return; + } + + int x=pixel2x(e->x()); + int y=pixel2y(e->y()); + + if (x < 0 || y < 0 || x >= levelMap_->width() || y >= levelMap_->height()) + return; + + if (e->button() == LeftButton && pathFinder_.canDrag(x, y)) { + QPainter paint(this); + changeCursor(&sizeAllCursor); + + if (levelMap_->goal(x, y)) + imageData_->brightTreasure(paint, x2pixel(x), y2pixel(y)); + else + imageData_->brightObject(paint, x2pixel(x), y2pixel(y)); + highlightX_ = x; + highlightY_ = y; + pathFinder_.updatePossibleDestinations(x, y); + + dragX_ = x2pixel(x); + dragY_ = y2pixel(y); + mousePosX_ = e->x() - dragX_; + mousePosY_ = e->y() - dragY_; + dragInProgress_ = true; + } + + Move *m; + switch (e->button()) { + case LeftButton: + m = pathFinder_.search(levelMap_, x, y); + if (m != 0) { + history_->add(m); + + startMoving(m); + } + break; + case MidButton: + undo(); + return; + break; + case RightButton: + push(x, y); + break; + + default: + return; + } +} + +void +PlayField::wheelEvent(QWheelEvent *e) { + wheelDelta_ += e->delta(); + + if (wheelDelta_ >= 120) { + wheelDelta_ %= 120; + redo(); + } else if (wheelDelta_ <= -120) { + wheelDelta_ = -(-wheelDelta_ % 120); + undo(); + } +} + +void +PlayField::mouseReleaseEvent(QMouseEvent *e) { + if (dragInProgress_) dragObject(e->x(), e->y()); +} + + +void +PlayField::focusInEvent(QFocusEvent *) { + //printf("PlayField::focusInEvent\n"); +} + +void +PlayField::focusOutEvent(QFocusEvent *) { + //printf("PlayField::focusOutEvent\n"); +} + +void +PlayField::leaveEvent(QEvent *) { + stopDrag(); +} + +void +PlayField::setSize(int w, int h) { + int sbarHeight = statusMetrics_.height(); + int sbarNumWidth = statusMetrics_.boundingRect("88888").width()+8; + int sbarLevelWidth = statusMetrics_.boundingRect(levelText_).width()+8; + int sbarStepsWidth = statusMetrics_.boundingRect(stepsText_).width()+8; + int sbarPushesWidth = statusMetrics_.boundingRect(pushesText_).width()+8; + + pnumRect_.setRect(w-sbarNumWidth, h-sbarHeight, sbarNumWidth, sbarHeight); + ptxtRect_.setRect(pnumRect_.x()-sbarPushesWidth, h-sbarHeight, sbarPushesWidth, sbarHeight); + snumRect_.setRect(ptxtRect_.x()-sbarNumWidth, h-sbarHeight, sbarNumWidth, sbarHeight); + stxtRect_.setRect(snumRect_.x()-sbarStepsWidth, h-sbarHeight, sbarStepsWidth, sbarHeight); + lnumRect_.setRect(stxtRect_.x()-sbarNumWidth, h-sbarHeight, sbarNumWidth, sbarHeight); + ltxtRect_.setRect(lnumRect_.x()-sbarLevelWidth, h-sbarHeight, sbarLevelWidth, sbarHeight); + collRect_.setRect(0, h-sbarHeight, ltxtRect_.x(), sbarHeight); + + collXpm_.resize(collRect_.size()); + ltxtXpm_.resize(ltxtRect_.size()); + lnumXpm_.resize(lnumRect_.size()); + stxtXpm_.resize(stxtRect_.size()); + snumXpm_.resize(snumRect_.size()); + ptxtXpm_.resize(ptxtRect_.size()); + pnumXpm_.resize(pnumRect_.size()); + + h -= sbarHeight; + + int cols = levelMap_->width(); + int rows = levelMap_->height(); + + // FIXME: the line below should not be needed + if (cols == 0 || rows == 0) return; + + int xsize = w / cols; + int ysize = h / rows; + + if (xsize < 8) xsize = 8; + if (ysize < 8) ysize = 8; + + size_ = imageData_->resize(xsize > ysize ? ysize : xsize); + + xOffs_ = (w - cols*size_) / 2; + yOffs_ = (h - rows*size_) / 2; + + + updateCollectionXpm(); + updateTextXpm(); + updateLevelXpm(); + updateStepsXpm(); + updatePushesXpm(); +} + +void +PlayField::nextLevel() { + if (levelMap_->level()+1 >= levelMap_->noOfLevels()) { + ModalLabel::message(i18n("\ +This is the last level in\n\ +the current collection."), this); + return; + } + if (levelMap_->level() >= levelMap_->completedLevels()) { + ModalLabel::message(i18n("\ +You have not completed\n\ +this level yet."), this); + return; + } + + level(levelMap_->level()+1); + levelChange(); + repaint(false); +} + +void +PlayField::previousLevel() { + if (levelMap_->level() <= 0) { + ModalLabel::message(i18n("\ +This is the first level in\n\ +the current collection."), this); + return; + } + level(levelMap_->level()-1); + levelChange(); + repaint(false); +} + +void +PlayField::undo() { + if (!canMoveNow()) return; + + startMoving(history_->deferUndo(levelMap_)); +} + +void +PlayField::redo() { + if (!canMoveNow()) return; + + startMoving(history_->deferRedo(levelMap_)); +} + +void +PlayField::restartLevel() { + stopMoving(); + history_->clear(); + level(levelMap_->level()); + updateStepsXpm(); + updatePushesXpm(); + repaint(false); +} + +void +PlayField::changeCollection(LevelCollection *collection) { + if (levelMap_->collection() == collection) return; + levelMap_->changeCollection(collection); + levelChange(); + //erase(collRect_); + repaint(false); +} + +void +PlayField::updateCollectionXpm() { + if (collXpm_.isNull()) return; + + QPainter paint(&collXpm_); + paint.setBrushOrigin(- collRect_.x(), - collRect_.y()); + paint.fillRect(0, 0, collRect_.width(), collRect_.height(), background_); + + paint.setFont(statusFont_); + paint.setPen(QColor(0,255,0)); + paint.drawText(0, 0, collRect_.width(), collRect_.height(), + AlignLeft, collectionName()); +} + +void +PlayField::updateTextXpm() { + if (ltxtXpm_.isNull()) return; + + QPainter paint; + + paint.begin(<xtXpm_); + paint.setBrushOrigin(- ltxtRect_.x(), - ltxtRect_.y()); + paint.fillRect(0, 0, ltxtRect_.width(), ltxtRect_.height(), background_); + paint.setFont(statusFont_); + paint.setPen(QColor(128,128,128)); + paint.drawText(0, 0, ltxtRect_.width(), ltxtRect_.height(), AlignLeft, levelText_); + paint.end(); + + paint.begin(&stxtXpm_); + paint.setBrushOrigin(- stxtRect_.x(), - stxtRect_.y()); + paint.fillRect(0, 0, stxtRect_.width(), stxtRect_.height(), background_); + paint.setFont(statusFont_); + paint.setPen(QColor(128,128,128)); + paint.drawText(0, 0, stxtRect_.width(), stxtRect_.height(), AlignLeft, stepsText_); + paint.end(); + + paint.begin(&ptxtXpm_); + paint.setBrushOrigin(- ptxtRect_.x(), - ptxtRect_.y()); + paint.fillRect(0, 0, ptxtRect_.width(), ptxtRect_.height(), background_); + paint.setFont(statusFont_); + paint.setPen(QColor(128,128,128)); + paint.drawText(0, 0, ptxtRect_.width(), ptxtRect_.height(), AlignLeft, pushesText_); + paint.end(); +} + +void +PlayField::updateLevelXpm() { + if (lnumXpm_.isNull()) return; + + QPainter paint(&lnumXpm_); + paint.setBrushOrigin(- lnumRect_.x(), - lnumRect_.y()); + paint.fillRect(0, 0, lnumRect_.width(), lnumRect_.height(), background_); + + QString str; + paint.setFont(statusFont_); + paint.setPen(QColor(255,0,0)); + paint.drawText(0, 0, lnumRect_.width(), lnumRect_.height(), + AlignLeft, str.sprintf("%05d", level()+1)); +} + +void +PlayField::updateStepsXpm() { + if (snumXpm_.isNull()) return; + + QPainter paint(&snumXpm_); + paint.setBrushOrigin(- snumRect_.x(), - snumRect_.y()); + paint.fillRect(0, 0, snumRect_.width(), snumRect_.height(), background_); + + QString str; + paint.setFont(statusFont_); + paint.setPen(QColor(255,0,0)); + paint.drawText(0, 0, snumRect_.width(), snumRect_.height(), + AlignLeft, str.sprintf("%05d", totalMoves())); +} + +void +PlayField::updatePushesXpm() { + if (pnumXpm_.isNull()) return; + + QPainter paint(&pnumXpm_); + paint.setBrushOrigin(- pnumRect_.x(), - pnumRect_.y()); + paint.fillRect(0, 0, pnumRect_.width(), pnumRect_.height(), background_); + + QString str; + paint.setFont(statusFont_); + paint.setPen(QColor(255,0,0)); + paint.drawText(0, 0, pnumRect_.width(), pnumRect_.height(), + AlignLeft, str.sprintf("%05d", totalPushes())); +} + + +void +PlayField::changeAnim(int num) +{ + assert(num >= 0 && num <= 3); + + animDelay_ = num; +} + +// FIXME: clean up bookmark stuff + +// static const int bookmark_id[] = { +// 0, 1, 8, 2, 9, 3, 5, 6, 7, 4 +// }; + +void +PlayField::setBookmark(Bookmark *bm) { + if (!levelMap_->goodLevel()) return; + + if (collection()->id() < 0) { + KMessageBox::sorry(this, i18n("Sorry, bookmarks for external levels\n" + "is not implemented yet.")); + return; + } + + bm->set(collection()->id(), levelMap_->level(), levelMap_->totalMoves(), history_); +} + +void +PlayField::goToBookmark(Bookmark *bm) { + level(bm->level()); + levelChange(); + if (!bm->goTo(levelMap_, history_)) fprintf(stderr, "Warning: bad bookmark\n"); + //updateLevelXpm(); + updateStepsXpm(); + updatePushesXpm(); + repaint(false); +} + +bool +PlayField::canMoveNow() { + if (moveInProgress_) return false; + if (!levelMap_->goodLevel()) { + ModalLabel::message(i18n("This level is broken"), this); + return false; + } + return true; +} 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 <[email protected]> + * + * 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 <[email protected]> + * + * 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 <[email protected]> + +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 <[email protected]> + * + * 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 <[email protected]> + * + * 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 Binary files differnew file mode 100644 index 00000000..25ff5810 --- /dev/null +++ b/ksokoban/data/hi128-app-ksokoban.png diff --git a/ksokoban/data/hi16-app-ksokoban.png b/ksokoban/data/hi16-app-ksokoban.png Binary files differnew file mode 100644 index 00000000..a6f1b41b --- /dev/null +++ b/ksokoban/data/hi16-app-ksokoban.png diff --git a/ksokoban/data/hi22-app-ksokoban.png b/ksokoban/data/hi22-app-ksokoban.png Binary files differnew file mode 100644 index 00000000..3239ddbf --- /dev/null +++ b/ksokoban/data/hi22-app-ksokoban.png diff --git a/ksokoban/data/hi32-app-ksokoban.png b/ksokoban/data/hi32-app-ksokoban.png Binary files differnew file mode 100644 index 00000000..f9050856 --- /dev/null +++ b/ksokoban/data/hi32-app-ksokoban.png diff --git a/ksokoban/data/hi48-app-ksokoban.png b/ksokoban/data/hi48-app-ksokoban.png Binary files differnew file mode 100644 index 00000000..9de2975d --- /dev/null +++ b/ksokoban/data/hi48-app-ksokoban.png diff --git a/ksokoban/data/hi64-app-ksokoban.png b/ksokoban/data/hi64-app-ksokoban.png Binary files differnew file mode 100644 index 00000000..1ddce158 --- /dev/null +++ b/ksokoban/data/hi64-app-ksokoban.png 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 [email protected] [email protected] +p +d + rm -f [email protected] + +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 <[email protected]> + * + * 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 <[email protected]> + +#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 Binary files differnew file mode 100644 index 00000000..fcac3f41 --- /dev/null +++ b/ksokoban/images/goal.png 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 <[email protected]> + +#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 Binary files differnew file mode 100644 index 00000000..267b3e45 --- /dev/null +++ b/ksokoban/images/halfstone_1.png 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 <[email protected]> + +#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 Binary files differnew file mode 100644 index 00000000..96144c60 --- /dev/null +++ b/ksokoban/images/halfstone_2.png 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 <[email protected]> + +#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 Binary files differnew file mode 100644 index 00000000..30994f96 --- /dev/null +++ b/ksokoban/images/halfstone_3.png 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 <[email protected]> + +#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 Binary files differnew file mode 100644 index 00000000..d8137006 --- /dev/null +++ b/ksokoban/images/halfstone_4.png 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 <[email protected]> + +#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 Binary files differnew file mode 100644 index 00000000..491e7ebf --- /dev/null +++ b/ksokoban/images/man.png 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 <[email protected]> + +#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 <[email protected]> + +#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 Binary files differnew file mode 100644 index 00000000..f6a842d0 --- /dev/null +++ b/ksokoban/images/object.png 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 <[email protected]> + +#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 Binary files differnew file mode 100644 index 00000000..c859047c --- /dev/null +++ b/ksokoban/images/saveman.png 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 <[email protected]> + +#include "goal.pov" +#include "man_common.inc" diff --git a/ksokoban/images/starfield.png b/ksokoban/images/starfield.png Binary files differnew file mode 100644 index 00000000..e09204be --- /dev/null +++ b/ksokoban/images/starfield.png diff --git a/ksokoban/images/stone_1.png b/ksokoban/images/stone_1.png Binary files differnew file mode 100644 index 00000000..221975ca --- /dev/null +++ b/ksokoban/images/stone_1.png 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 <[email protected]> + +#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 Binary files differnew file mode 100644 index 00000000..71ea2962 --- /dev/null +++ b/ksokoban/images/stone_2.png 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 <[email protected]> + +#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 Binary files differnew file mode 100644 index 00000000..a85c5eec --- /dev/null +++ b/ksokoban/images/stone_3.png 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 <[email protected]> + +#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 Binary files differnew file mode 100644 index 00000000..5f17e094 --- /dev/null +++ b/ksokoban/images/stone_4.png 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 <[email protected]> + +#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 Binary files differnew file mode 100644 index 00000000..7a29b25d --- /dev/null +++ b/ksokoban/images/stone_5.png 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 <[email protected]> + +#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 Binary files differnew file mode 100644 index 00000000..22eefb05 --- /dev/null +++ b/ksokoban/images/stone_6.png 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 <[email protected]> + +#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 <[email protected]> + +#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 Binary files differnew file mode 100644 index 00000000..2560f1ef --- /dev/null +++ b/ksokoban/images/treasure.png 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 <[email protected]> + +#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 Binary files differnew file mode 100644 index 00000000..029e5c72 --- /dev/null +++ b/ksokoban/levels/level.data 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 <[email protected]> + * + * 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, + "[email protected]", + "http://hem.passagen.se/awl/"); + aboutData.addCredit("David W. Skinner", + I18N_NOOP("For contributing the Sokoban levels included in this game"), + "[email protected]", + "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; +} |