summaryrefslogtreecommitdiffstats
path: root/kpat/dealer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kpat/dealer.cpp')
-rw-r--r--kpat/dealer.cpp1455
1 files changed, 1455 insertions, 0 deletions
diff --git a/kpat/dealer.cpp b/kpat/dealer.cpp
new file mode 100644
index 00000000..2100ae2e
--- /dev/null
+++ b/kpat/dealer.cpp
@@ -0,0 +1,1455 @@
+#include "dealer.h"
+#include <kstaticdeleter.h>
+#include <kdebug.h>
+#include "deck.h"
+#include <assert.h>
+#include "kmainwindow.h"
+#include <kapplication.h>
+#include <kpixmapeffect.h>
+#include <qtimer.h>
+#include <kaction.h>
+#include <klocale.h>
+#include "cardmaps.h"
+#include "speeds.h"
+#include <kconfig.h>
+#include "version.h"
+
+
+// ================================================================
+// class MoveHint
+
+
+MoveHint::MoveHint(Card *card, Pile *to, bool d)
+{
+ m_card = card;
+ m_to = to;
+ m_dropiftarget = d;
+}
+
+
+// ================================================================
+// class DealerInfoList
+
+
+DealerInfoList *DealerInfoList::_self = 0;
+static KStaticDeleter<DealerInfoList> dl;
+
+
+DealerInfoList *DealerInfoList::self()
+{
+ if (!_self)
+ _self = dl.setObject(_self, new DealerInfoList());
+ return _self;
+}
+
+void DealerInfoList::add(DealerInfo *dealer)
+{
+ list.append(dealer);
+}
+
+
+// ================================================================
+// class Dealer
+
+
+Dealer *Dealer::s_instance = 0;
+
+
+Dealer::Dealer( KMainWindow* _parent , const char* _name )
+ : QCanvasView( 0, _parent, _name ),
+ towait(0),
+ myActions(0),
+ ademo(0),
+ ahint(0),
+ aredeal(0),
+ takeTargets(false),
+ _won(false),
+ _waiting(0),
+ stop_demo_next(false),
+ _autodrop(true),
+ _gameRecorded(false)
+{
+ setResizePolicy(QScrollView::Manual);
+ setVScrollBarMode(AlwaysOff);
+ setHScrollBarMode(AlwaysOff);
+
+ setGameNumber(kapp->random());
+ myCanvas.setAdvancePeriod(30);
+ // myCanvas.setBackgroundColor( darkGreen );
+ setCanvas(&myCanvas);
+ myCanvas.setDoubleBuffering(true);
+
+ undoList.setAutoDelete(true);
+
+ demotimer = new QTimer(this);
+
+ connect(demotimer, SIGNAL(timeout()), SLOT(demo()));
+
+ assert(!s_instance);
+ s_instance = this;
+}
+
+
+const Dealer *Dealer::instance()
+{
+ return s_instance;
+}
+
+
+void Dealer::setBackgroundPixmap(const QPixmap &background, const QColor &midcolor)
+{
+ _midcolor = midcolor;
+ canvas()->setBackgroundPixmap(background);
+ for (PileList::Iterator it = piles.begin(); it != piles.end(); ++it) {
+ (*it)->resetCache();
+ (*it)->initSizes();
+ }
+}
+
+void Dealer::setupActions() {
+
+ QPtrList<KAction> actionlist;
+
+ kdDebug(11111) << "setupActions " << actions() << endl;
+
+ if (actions() & Dealer::Hint) {
+
+ ahint = new KAction( i18n("&Hint"), QString::fromLatin1("wizard"), Key_H, this,
+ SLOT(hint()),
+ parent()->actionCollection(), "game_hint");
+ actionlist.append(ahint);
+ } else
+ ahint = 0;
+
+ if (actions() & Dealer::Demo) {
+ ademo = new KToggleAction( i18n("&Demo"), QString::fromLatin1("1rightarrow"), CTRL+Key_D, this,
+ SLOT(toggleDemo()),
+ parent()->actionCollection(), "game_demo");
+ actionlist.append(ademo);
+ } else
+ ademo = 0;
+
+ if (actions() & Dealer::Redeal) {
+ aredeal = new KAction (i18n("&Redeal"), QString::fromLatin1("queue"), 0, this,
+ SLOT(redeal()),
+ parent()->actionCollection(), "game_redeal");
+ actionlist.append(aredeal);
+ } else
+ aredeal = 0;
+
+ parent()->guiFactory()->plugActionList( parent(), QString::fromLatin1("game_actions"), actionlist);
+}
+
+Dealer::~Dealer()
+{
+ if (!_won)
+ countLoss();
+ clearHints();
+ parent()->guiFactory()->unplugActionList( parent(), QString::fromLatin1("game_actions"));
+
+ while (!piles.isEmpty())
+ delete piles.first(); // removes itself
+
+ if (s_instance == this)
+ s_instance = 0;
+}
+
+KMainWindow *Dealer::parent() const
+{
+ return dynamic_cast<KMainWindow*>(QCanvasView::parent());
+}
+
+
+// ----------------------------------------------------------------
+
+
+void Dealer::hint()
+{
+ unmarkAll();
+ clearHints();
+ getHints();
+ for (HintList::ConstIterator it = hints.begin(); it != hints.end(); ++it)
+ mark((*it)->card());
+ clearHints();
+ canvas()->update();
+}
+
+
+void Dealer::getHints()
+{
+ for (PileList::Iterator it = piles.begin(); it != piles.end(); ++it)
+ {
+ if (!takeTargetForHints() && (*it)->target())
+ continue;
+
+ Pile *store = *it;
+ if (store->isEmpty())
+ continue;
+// kdDebug(11111) << "trying " << store->top()->name() << endl;
+
+ CardList cards = store->cards();
+ while (cards.count() && !cards.first()->realFace()) cards.remove(cards.begin());
+
+ CardList::Iterator iti = cards.begin();
+ while (iti != cards.end())
+ {
+ if (store->legalRemove(*iti)) {
+// kdDebug(11111) << "could remove " << (*iti)->name() << endl;
+ for (PileList::Iterator pit = piles.begin(); pit != piles.end(); ++pit)
+ {
+ Pile *dest = *pit;
+ if (dest == store)
+ continue;
+ if (store->indexOf(*iti) == 0 && dest->isEmpty() && !dest->target())
+ continue;
+ if (!dest->legalAdd(cards))
+ continue;
+
+ bool old_prefer = checkPrefering( dest->checkIndex(), dest, cards );
+ if (!takeTargetForHints() && dest->target())
+ newHint(new MoveHint(*iti, dest));
+ else {
+ store->hideCards(cards);
+ // if it could be here as well, then it's no use
+ if ((store->isEmpty() && !dest->isEmpty()) || !store->legalAdd(cards))
+ newHint(new MoveHint(*iti, dest));
+ else {
+ if (old_prefer && !checkPrefering( store->checkIndex(),
+ store, cards ))
+ { // if checkPrefers says so, we add it nonetheless
+ newHint(new MoveHint(*iti, dest));
+ }
+ }
+ store->unhideCards(cards);
+ }
+ }
+ }
+ cards.remove(iti);
+ iti = cards.begin();
+ }
+ }
+}
+
+bool Dealer::checkPrefering( int /*checkIndex*/, const Pile *, const CardList& ) const
+{
+ return false;
+}
+
+void Dealer::clearHints()
+{
+ for (HintList::Iterator it = hints.begin(); it != hints.end(); ++it)
+ delete *it;
+ hints.clear();
+}
+
+void Dealer::newHint(MoveHint *mh)
+{
+ hints.append(mh);
+}
+
+bool Dealer::isMoving(Card *c) const
+{
+ return movingCards.find(c) != movingCards.end();
+}
+
+void Dealer::contentsMouseMoveEvent(QMouseEvent* e)
+{
+ if (movingCards.isEmpty())
+ return;
+
+ moved = true;
+
+ for (CardList::Iterator it = movingCards.begin(); it != movingCards.end(); ++it)
+ {
+ (*it)->moveBy(e->pos().x() - moving_start.x(),
+ e->pos().y() - moving_start.y());
+ }
+
+ PileList sources;
+ QCanvasItemList list = canvas()->collisions(movingCards.first()->rect());
+
+ for (QCanvasItemList::Iterator it = list.begin(); it != list.end(); ++it)
+ {
+ if ((*it)->rtti() == Card::RTTI) {
+ Card *c = dynamic_cast<Card*>(*it);
+ assert(c);
+ if (!c->isFaceUp())
+ continue;
+ if (c->source() == movingCards.first()->source())
+ continue;
+ if (sources.findIndex(c->source()) != -1)
+ continue;
+ sources.append(c->source());
+ } else {
+ if ((*it)->rtti() == Pile::RTTI) {
+ Pile *p = static_cast<Pile*>(*it);
+ if (p->isEmpty() && !sources.contains(p))
+ sources.append(p);
+ } else {
+ kdDebug(11111) << "unknown object " << *it << " " << (*it)->rtti() << endl;
+ }
+ }
+ }
+
+ // TODO some caching of the results
+ unmarkAll();
+
+ for (PileList::Iterator it = sources.begin(); it != sources.end(); ++it)
+ {
+ bool b = (*it)->legalAdd(movingCards);
+ if (b) {
+ if ((*it)->isEmpty()) {
+ (*it)->setSelected(true);
+ marked.append(*it);
+ } else {
+ mark((*it)->top());
+ }
+ }
+ }
+
+ moving_start = e->pos();
+ canvas()->update();
+}
+
+void Dealer::mark(Card *c)
+{
+ c->setSelected(true);
+ if (!marked.contains(c))
+ marked.append(c);
+}
+
+void Dealer::unmarkAll()
+{
+ for (QCanvasItemList::Iterator it = marked.begin(); it != marked.end(); ++it)
+ {
+ (*it)->setSelected(false);
+ }
+ marked.clear();
+}
+
+void Dealer::contentsMousePressEvent(QMouseEvent* e)
+{
+ unmarkAll();
+ stopDemo();
+ if (waiting())
+ return;
+
+ QCanvasItemList list = canvas()->collisions(e->pos());
+
+ kdDebug(11111) << "mouse pressed " << list.count() << " " << canvas()->allItems().count() << endl;
+ moved = false;
+
+ if (!list.count())
+ return;
+
+ if (e->button() == LeftButton) {
+ if (list.first()->rtti() == Card::RTTI) {
+ Card *c = dynamic_cast<Card*>(list.first());
+ assert(c);
+ CardList mycards = c->source()->cardPressed(c);
+ for (CardList::Iterator it = mycards.begin(); it != mycards.end(); ++it)
+ (*it)->setAnimated(false);
+ movingCards = mycards;
+ moving_start = e->pos();
+ }
+ return;
+ }
+
+ if (e->button() == RightButton) {
+ if (list.first()->rtti() == Card::RTTI) {
+ Card *preview = dynamic_cast<Card*>(list.first());
+ assert(preview);
+ if (!preview->animated() && !isMoving(preview))
+ preview->getUp();
+ }
+ return;
+ }
+
+ // if it's nothing else, we move the cards back
+ contentsMouseReleaseEvent(e);
+
+}
+
+class Hit {
+public:
+ Pile *source;
+ QRect intersect;
+ bool top;
+};
+typedef QValueList<Hit> HitList;
+
+void Dealer::contentsMouseReleaseEvent( QMouseEvent *e)
+{
+ if (!moved) {
+ if (!movingCards.isEmpty()) {
+ movingCards.first()->source()->moveCardsBack(movingCards);
+ movingCards.clear();
+ }
+ QCanvasItemList list = canvas()->collisions(e->pos());
+ if (list.isEmpty())
+ return;
+ QCanvasItemList::Iterator it = list.begin();
+ if ((*it)->rtti() == Card::RTTI) {
+ Card *c = dynamic_cast<Card*>(*it);
+ assert(c);
+ if (!c->animated()) {
+ if ( cardClicked(c) ) {
+ countGame();
+ }
+ takeState();
+ canvas()->update();
+ }
+ return;
+ }
+ if ((*it)->rtti() == Pile::RTTI) {
+ Pile *c = dynamic_cast<Pile*>(*it);
+ assert(c);
+ pileClicked(c);
+ takeState();
+ canvas()->update();
+ return;
+ }
+ }
+
+ if (!movingCards.count())
+ return;
+ Card *c = static_cast<Card*>(movingCards.first());
+ assert(c);
+
+ unmarkAll();
+
+ QCanvasItemList list = canvas()->collisions(movingCards.first()->rect());
+ HitList sources;
+
+ for (QCanvasItemList::Iterator it = list.begin(); it != list.end(); ++it)
+ {
+ if ((*it)->rtti() == Card::RTTI) {
+ Card *c = dynamic_cast<Card*>(*it);
+ assert(c);
+ if (!c->isFaceUp())
+ continue;
+ if (c->source() == movingCards.first()->source())
+ continue;
+ Hit t;
+ t.source = c->source();
+ t.intersect = c->rect().intersect(movingCards.first()->rect());
+ t.top = (c == c->source()->top());
+
+ bool found = false;
+ for (HitList::Iterator hi = sources.begin(); hi != sources.end(); ++hi)
+ {
+ if ((*hi).source == c->source()) {
+ found = true;
+ if ((*hi).intersect.width() * (*hi).intersect.height() >
+ t.intersect.width() * t.intersect.height())
+ {
+ (*hi).intersect = t.intersect;
+ (*hi).top |= t.top;
+ }
+ }
+ }
+ if (found)
+ continue;
+
+ sources.append(t);
+ } else {
+ if ((*it)->rtti() == Pile::RTTI) {
+ Pile *p = static_cast<Pile*>(*it);
+ if (p->isEmpty())
+ {
+ Hit t;
+ t.source = p;
+ t.intersect = p->rect().intersect(movingCards.first()->rect());
+ t.top = true;
+ sources.append(t);
+ }
+ } else {
+ kdDebug(11111) << "unknown object " << *it << " " << (*it)->rtti() << endl;
+ }
+ }
+ }
+
+ for (HitList::Iterator it = sources.begin(); it != sources.end(); )
+ {
+ if (!(*it).source->legalAdd(movingCards))
+ it = sources.remove(it);
+ else
+ ++it;
+ }
+
+ if (sources.isEmpty()) {
+ c->source()->moveCardsBack(movingCards);
+ } else {
+ HitList::Iterator best = sources.begin();
+ HitList::Iterator it = best;
+ for (++it; it != sources.end(); ++it )
+ {
+ if ((*it).intersect.width() * (*it).intersect.height() >
+ (*best).intersect.width() * (*best).intersect.height()
+ || ((*it).top && !(*best).top))
+ {
+ best = it;
+ }
+ }
+ countGame();
+ c->source()->moveCards(movingCards, (*best).source);
+ takeState();
+ }
+ movingCards.clear();
+ canvas()->update();
+}
+
+void Dealer::contentsMouseDoubleClickEvent( QMouseEvent*e )
+{
+ stopDemo();
+ unmarkAll();
+ if (waiting())
+ return;
+
+ if (!movingCards.isEmpty()) {
+ movingCards.first()->source()->moveCardsBack(movingCards);
+ movingCards.clear();
+ }
+ QCanvasItemList list = canvas()->collisions(e->pos());
+ if (list.isEmpty())
+ return;
+ QCanvasItemList::Iterator it = list.begin();
+ if ((*it)->rtti() != Card::RTTI)
+ return;
+ Card *c = dynamic_cast<Card*>(*it);
+ assert(c);
+ if (!c->animated()) {
+ if ( cardDblClicked(c) ) {
+ countGame();
+ }
+ takeState();
+ }
+}
+
+QSize Dealer::minimumCardSize() const
+{
+ return minsize;
+}
+
+void Dealer::resizeEvent(QResizeEvent *e)
+{
+ int x = width();
+ int y = height();
+ int hs = horizontalScrollBar()->sizeHint().height();
+ int vs = verticalScrollBar()->sizeHint().width();
+
+ int mx = minsize.width();
+ int my = minsize.height();
+
+ int dx = x;
+ int dy = y;
+ bool showh = false;
+ bool showv = false;
+
+ if (mx > x) {
+ dx = mx;
+ if (my + vs < y)
+ dy -= vs;
+ else {
+ showv = true;
+ }
+ showh = true;
+ } else if (my > y) {
+ dy = my;
+ if (mx + hs < x)
+ dx -= hs;
+ else
+ showh = true;
+ showv = true;
+ }
+ canvas()->resize(dx, dy);
+ resizeContents(dx, dy);
+ setVScrollBarMode(showv ? AlwaysOn : AlwaysOff);
+ setHScrollBarMode(showh ? AlwaysOn : AlwaysOff);
+
+ if (!e)
+ updateScrollBars();
+ else
+ QCanvasView::resizeEvent(e);
+}
+
+bool Dealer::cardClicked(Card *c) {
+ return c->source()->cardClicked(c);
+}
+
+void Dealer::pileClicked(Pile *c) {
+ c->cardClicked(0);
+}
+
+bool Dealer::cardDblClicked(Card *c)
+{
+ if (c->source()->cardDblClicked(c))
+ return true;
+
+ if (c->animated())
+ return false;
+
+ if (c == c->source()->top() && c->realFace()) {
+ Pile *tgt = findTarget(c);
+ if (tgt) {
+ CardList empty;
+ empty.append(c);
+ c->source()->moveCards(empty, tgt);
+ canvas()->update();
+ return true;
+ }
+ }
+ return false;
+}
+
+void Dealer::startNew()
+{
+ if (!_won)
+ countLoss();
+ if ( ahint )
+ ahint->setEnabled( true );
+ if ( ademo )
+ ademo->setEnabled( true );
+ if ( aredeal )
+ aredeal->setEnabled( true );
+ toldAboutLostGame = false;
+ minsize = QSize(0,0);
+ _won = false;
+ _waiting = 0;
+ _gameRecorded=false;
+ kdDebug(11111) << "startNew stopDemo\n";
+ stopDemo();
+ kdDebug(11111) << "startNew unmarkAll\n";
+ unmarkAll();
+ kdDebug(11111) << "startNew setAnimated(false)\n";
+ QCanvasItemList list = canvas()->allItems();
+ for (QCanvasItemList::Iterator it = list.begin(); it != list.end(); ++it) {
+ if ((*it)->rtti() == Card::RTTI)
+ static_cast<Card*>(*it)->disconnect();
+
+ (*it)->setAnimated(true);
+ (*it)->setAnimated(false);
+ }
+
+ undoList.clear();
+ emit undoPossible(false);
+ emit updateMoves();
+ kdDebug(11111) << "startNew restart\n";
+ restart();
+ takeState();
+ Card *towait = 0;
+ for (QCanvasItemList::Iterator it = list.begin(); it != list.end(); ++it) {
+ if ((*it)->rtti() == Card::RTTI) {
+ towait = static_cast<Card*>(*it);
+ if (towait->animated())
+ break;
+ }
+ }
+
+ kdDebug(11111) << "startNew takeState\n";
+ if (!towait)
+ takeState();
+ else
+ connect(towait, SIGNAL(stoped(Card*)), SLOT(slotTakeState(Card *)));
+ resizeEvent(0);
+}
+
+void Dealer::slotTakeState(Card *c) {
+ if (c)
+ c->disconnect();
+ takeState();
+}
+
+void Dealer::enlargeCanvas(QCanvasRectangle *c)
+{
+ if (!c->isVisible() || c->animated())
+ return;
+
+ bool changed = false;
+
+ if (c->x() + c->width() + 10 > minsize.width()) {
+ minsize.setWidth(int(c->x()) + c->width() + 10);
+ changed = true;
+ }
+ if (c->y() + c->height() + 10 > minsize.height()) {
+ minsize.setHeight(int(c->y()) + c->height() + 10);
+ changed = true;
+ }
+ if (changed)
+ resizeEvent(0);
+}
+
+class CardState {
+public:
+ Card *it;
+ Pile *source;
+ double x;
+ double y;
+ double z;
+ bool faceup;
+ bool tookdown;
+ int source_index;
+ CardState() {}
+public:
+ // as every card is only once we can sort after the card.
+ // < is the same as <= in that context. == is different
+ bool operator<(const CardState &rhs) const { return it < rhs.it; }
+ bool operator<=(const CardState &rhs) const { return it <= rhs.it; }
+ bool operator>(const CardState &rhs) const { return it > rhs.it; }
+ bool operator>=(const CardState &rhs) const { return it > rhs.it; }
+ bool operator==(const CardState &rhs) const {
+ return (it == rhs.it && source == rhs.source && x == rhs.x &&
+ y == rhs.y && z == rhs.z && faceup == rhs.faceup
+ && source_index == rhs.source_index && tookdown == rhs.tookdown);
+ }
+ void fillNode(QDomElement &e) const {
+ e.setAttribute("value", it->rank());
+ e.setAttribute("suit", it->suit());
+ e.setAttribute("source", source->index());
+ e.setAttribute("x", x);
+ e.setAttribute("y", y);
+ e.setAttribute("z", z);
+ e.setAttribute("faceup", faceup);
+ e.setAttribute("tookdown", tookdown);
+ e.setAttribute("source_index", source_index);
+ }
+};
+
+typedef class QValueList<CardState> CardStateList;
+
+bool operator==( const State & st1, const State & st2) {
+ return st1.cards == st2.cards && st1.gameData == st2.gameData;
+}
+
+State *Dealer::getState()
+{
+ QCanvasItemList list = canvas()->allItems();
+ State * st = new State;
+
+ for (QCanvasItemList::ConstIterator it = list.begin(); it != list.end(); ++it)
+ {
+ if ((*it)->rtti() == Card::RTTI) {
+ Card *c = dynamic_cast<Card*>(*it);
+ assert(c);
+ CardState s;
+ s.it = c;
+ s.source = c->source();
+ if (!s.source) {
+ kdDebug(11111) << c->name() << " has no parent\n";
+ assert(false);
+ }
+ s.source_index = c->source()->indexOf(c);
+ s.x = c->realX();
+ s.y = c->realY();
+ s.z = c->realZ();
+ s.faceup = c->realFace();
+ s.tookdown = c->takenDown();
+ st->cards.append(s);
+ }
+ }
+ qHeapSort(st->cards);
+
+ // Game specific information
+ st->gameData = getGameState( );
+
+ return st;
+}
+
+void Dealer::setState(State *st)
+{
+ CardStateList * n = &st->cards;
+ QCanvasItemList list = canvas()->allItems();
+
+ for (QCanvasItemList::Iterator it = list.begin(); it != list.end(); ++it)
+ {
+ if ((*it)->rtti() == Pile::RTTI) {
+ Pile *p = dynamic_cast<Pile*>(*it);
+ assert(p);
+ CardList cards = p->cards();
+ for (CardList::Iterator it = cards.begin(); it != cards.end(); ++it)
+ (*it)->setTakenDown(p->target());
+ p->clear();
+ }
+ }
+
+ for (CardStateList::ConstIterator it = n->begin(); it != n->end(); ++it)
+ {
+ Card *c = (*it).it;
+ CardState s = *it;
+ bool target = c->takenDown(); // abused
+ s.source->add(c, s.source_index);
+ c->setVisible(s.source->isVisible());
+ c->setAnimated(false);
+ c->setX(s.x);
+ c->setY(s.y);
+ c->setZ(int(s.z));
+ c->setTakenDown(s.tookdown || (target && !s.source->target()));
+ c->turn(s.faceup);
+ }
+
+ // restore game-specific information
+ setGameState( st->gameData );
+
+ delete st;
+ canvas()->update();
+}
+
+void Dealer::takeState()
+{
+ kdDebug(11111) << "takeState\n";
+
+ State *n = getState();
+
+ if (!undoList.count()) {
+ emit updateMoves();
+ undoList.append(n);
+ } else {
+ State *old = undoList.last();
+
+ if (*old == *n) {
+ delete n;
+ n = 0;
+ } else {
+ emit updateMoves();
+ undoList.append(n);
+ }
+ }
+
+ if (n) {
+ if (isGameWon()) {
+ won();
+ return;
+ }
+ else if (isGameLost() && !toldAboutLostGame) {
+ if ( ahint )
+ ahint->setEnabled( false );
+ if ( ademo )
+ ademo->setEnabled( false );
+ if ( aredeal )
+ aredeal->setEnabled( false );
+ QTimer::singleShot(400, this, SIGNAL(gameLost()));
+ toldAboutLostGame = true;
+ return;
+ }
+ }
+ if (!demoActive() && !waiting())
+ QTimer::singleShot(TIME_BETWEEN_MOVES, this, SLOT(startAutoDrop()));
+
+ emit undoPossible(undoList.count() > 1 && !waiting());
+}
+
+void Dealer::saveGame(QDomDocument &doc) {
+ QDomElement dealer = doc.createElement("dealer");
+ doc.appendChild(dealer);
+ dealer.setAttribute("id", _id);
+ dealer.setAttribute("number", QString::number(gameNumber()));
+ QString data = getGameState();
+ if (!data.isEmpty())
+ dealer.setAttribute("data", data);
+ dealer.setAttribute("moves", QString::number(getMoves()));
+
+ bool taken[1000];
+ memset(taken, 0, sizeof(taken));
+
+ QCanvasItemList list = canvas()->allItems();
+ for (QCanvasItemList::Iterator it = list.begin(); it != list.end(); ++it)
+ {
+ if ((*it)->rtti() == Pile::RTTI) {
+ Pile *p = dynamic_cast<Pile*>(*it);
+ assert(p);
+ if (taken[p->index()]) {
+ kdDebug(11111) << "pile index " << p->index() << " taken twice\n";
+ return;
+ }
+ taken[p->index()] = true;
+
+ QDomElement pile = doc.createElement("pile");
+ pile.setAttribute("index", p->index());
+
+ CardList cards = p->cards();
+ for (CardList::Iterator it = cards.begin();
+ it != cards.end();
+ ++it)
+ {
+ QDomElement card = doc.createElement("card");
+ card.setAttribute("suit", (*it)->suit());
+ card.setAttribute("value", (*it)->rank());
+ card.setAttribute("faceup", (*it)->isFaceUp());
+ card.setAttribute("x", (*it)->realX());
+ card.setAttribute("y", (*it)->realY());
+ card.setAttribute("z", (*it)->realZ());
+ card.setAttribute("name", (*it)->name());
+ pile.appendChild(card);
+ }
+ dealer.appendChild(pile);
+ }
+ }
+
+ /*
+ QDomElement eList = doc.createElement("undo");
+
+ QPtrListIterator<State> it(undoList);
+ for (; it.current(); ++it)
+ {
+ State *n = it.current();
+ QDomElement state = doc.createElement("state");
+ if (!n->gameData.isEmpty())
+ state.setAttribute("data", n->gameData);
+ QDomElement cards = doc.createElement("cards");
+ for (QValueList<CardState>::ConstIterator it2 = n->cards.begin();
+ it2 != n->cards.end(); ++it2)
+ {
+ QDomElement item = doc.createElement("item");
+ (*it2).fillNode(item);
+ cards.appendChild(item);
+ }
+ state.appendChild(cards);
+ eList.appendChild(state);
+ }
+ dealer.appendChild(eList);
+ */
+ // kdDebug(11111) << doc.toString() << endl;
+}
+
+void Dealer::openGame(QDomDocument &doc)
+{
+ unmarkAll();
+ QDomElement dealer = doc.documentElement();
+
+ setGameNumber(dealer.attribute("number").toULong());
+ undoList.clear();
+
+ QDomNodeList piles = dealer.elementsByTagName("pile");
+
+ QCanvasItemList list = canvas()->allItems();
+
+ CardList cards;
+ for (QCanvasItemList::ConstIterator it = list.begin(); it != list.end(); ++it)
+ if ((*it)->rtti() == Card::RTTI)
+ cards.append(static_cast<Card*>(*it));
+
+ Deck::deck()->collectAndShuffle();
+
+ for (QCanvasItemList::Iterator it = list.begin(); it != list.end(); ++it)
+ {
+ if ((*it)->rtti() == Pile::RTTI)
+ {
+ Pile *p = dynamic_cast<Pile*>(*it);
+ assert(p);
+
+ for (uint i = 0; i < piles.count(); ++i)
+ {
+ QDomElement pile = piles.item(i).toElement();
+ if (pile.attribute("index").toInt() == p->index())
+ {
+ QDomNodeList pcards = pile.elementsByTagName("card");
+ for (uint j = 0; j < pcards.count(); ++j)
+ {
+ QDomElement card = pcards.item(j).toElement();
+ Card::Suit s = static_cast<Card::Suit>(card.attribute("suit").toInt());
+ Card::Rank v = static_cast<Card::Rank>(card.attribute("value").toInt());
+
+ for (CardList::Iterator it2 = cards.begin();
+ it2 != cards.end(); ++it2)
+ {
+ if ((*it2)->suit() == s && (*it2)->rank() == v) {
+ if (QString((*it2)->name()) == "Diamonds Eight") {
+ kdDebug(11111) << i << " " << j << endl;
+ }
+ p->add(*it2);
+ (*it2)->setAnimated(false);
+ (*it2)->turn(card.attribute("faceup").toInt());
+ (*it2)->setX(card.attribute("x").toInt());
+ (*it2)->setY(card.attribute("y").toInt());
+ (*it2)->setZ(card.attribute("z").toInt());
+ (*it2)->setVisible(p->isVisible());
+ cards.remove(it2);
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ setGameState( dealer.attribute("data") );
+
+ if (undoList.count() > 1) {
+ setState(undoList.take(undoList.count() - 1));
+ takeState(); // copying it again
+ emit undoPossible(undoList.count() > 1);
+ }
+
+ emit updateMoves();
+ takeState();
+}
+
+void Dealer::undo()
+{
+ unmarkAll();
+ stopDemo();
+ if (undoList.count() > 1) {
+ undoList.removeLast(); // the current state
+ setState(undoList.take(undoList.count() - 1));
+ emit updateMoves();
+ takeState(); // copying it again
+ emit undoPossible(undoList.count() > 1);
+ if ( toldAboutLostGame ) { // everything's possible again
+ if ( ahint )
+ ahint->setEnabled( true );
+ if ( ademo )
+ ademo->setEnabled( true );
+ toldAboutLostGame = false;
+ }
+ }
+}
+
+Pile *Dealer::findTarget(Card *c)
+{
+ if (!c)
+ return 0;
+
+ CardList empty;
+ empty.append(c);
+ for (PileList::ConstIterator it = piles.begin(); it != piles.end(); ++it)
+ {
+ if (!(*it)->target())
+ continue;
+ if ((*it)->legalAdd(empty))
+ return *it;
+ }
+ return 0;
+}
+
+void Dealer::setWaiting(bool w)
+{
+ if (w)
+ _waiting++;
+ else
+ _waiting--;
+ emit undoPossible(!waiting());
+ kdDebug(11111) << "setWaiting " << w << " " << _waiting << endl;
+}
+
+void Dealer::setAutoDropEnabled(bool a)
+{
+ _autodrop = a;
+ QTimer::singleShot(TIME_BETWEEN_MOVES, this, SLOT(startAutoDrop()));
+}
+
+bool Dealer::startAutoDrop()
+{
+ if (!autoDrop())
+ return false;
+
+ QCanvasItemList list = canvas()->allItems();
+
+ for (QCanvasItemList::ConstIterator it = list.begin(); it != list.end(); ++it)
+ if ((*it)->animated()) {
+ QTimer::singleShot(TIME_BETWEEN_MOVES, this, SLOT(startAutoDrop()));
+ return true;
+ }
+
+ kdDebug(11111) << "startAutoDrop\n";
+
+ unmarkAll();
+ clearHints();
+ getHints();
+ for (HintList::ConstIterator it = hints.begin(); it != hints.end(); ++it) {
+ MoveHint *mh = *it;
+ if (mh->pile()->target() && mh->dropIfTarget() && !mh->card()->takenDown()) {
+ setWaiting(true);
+ Card *t = mh->card();
+ CardList cards = mh->card()->source()->cards();
+ while (cards.count() && cards.first() != t) cards.remove(cards.begin());
+ t->setAnimated(false);
+ t->turn(true);
+ int x = int(t->x());
+ int y = int(t->y());
+ t->source()->moveCards(cards, mh->pile());
+ t->move(x, y);
+ kdDebug(11111) << "autodrop " << t->name() << endl;
+ t->moveTo(int(t->source()->x()), int(t->source()->y()), int(t->z()), STEPS_AUTODROP);
+ connect(t, SIGNAL(stoped(Card*)), SLOT(waitForAutoDrop(Card*)));
+ return true;
+ }
+ }
+ clearHints();
+ return false;
+}
+
+void Dealer::waitForAutoDrop(Card * c) {
+ kdDebug(11111) << "waitForAutoDrop " << c->name() << endl;
+ setWaiting(false);
+ c->disconnect();
+ takeState();
+}
+
+long Dealer::gameNumber() const
+{
+ return gamenumber;
+}
+
+void Dealer::setGameNumber(long gmn)
+{
+ // Deal in the range of 1 to INT_MAX.
+ gamenumber = ((gmn < 1) ? 1 : ((gmn > 0x7FFFFFFF) ? 0x7FFFFFFF : gmn));
+}
+
+void Dealer::addPile(Pile *p)
+{
+ piles.append(p);
+}
+
+void Dealer::removePile(Pile *p)
+{
+ piles.remove(p);
+}
+
+void Dealer::stopDemo()
+{
+ kdDebug(11111) << "stopDemo " << waiting() << " " << stop_demo_next << endl;
+ if (waiting()) {
+ stop_demo_next = true;
+ return;
+ } else stop_demo_next = false;
+
+ if (towait == (Card*)-1)
+ towait = 0;
+
+ if (towait) {
+ towait->disconnect();
+ towait = 0;
+ }
+ demotimer->stop();
+ if (ademo)
+ ademo->setChecked(false);
+}
+
+bool Dealer::demoActive() const
+{
+ return (towait || demotimer->isActive());
+}
+
+void Dealer::toggleDemo()
+{
+ if (demoActive()) {
+ stopDemo();
+ } else
+ demo();
+}
+
+class CardPtr
+{
+ public:
+ Card *ptr;
+};
+
+bool operator <(const CardPtr &p1, const CardPtr &p2)
+{
+ return ( p1.ptr->z() < p2.ptr->z() );
+}
+
+void Dealer::won()
+{
+ if (_won)
+ return;
+ _won = true;
+
+ // update score, 'win' in demo mode also counts (keep it that way?)
+ { // wrap in own scope to make KConfigGroupSave work
+ KConfig *config = kapp->config();
+ KConfigGroupSaver kcs(config, scores_group);
+ unsigned int n = config->readUnsignedNumEntry(QString("won%1").arg(_id),0) + 1;
+ config->writeEntry(QString("won%1").arg(_id),n);
+ n = config->readUnsignedNumEntry(QString("winstreak%1").arg(_id),0) + 1;
+ config->writeEntry(QString("winstreak%1").arg(_id),n);
+ unsigned int m = config->readUnsignedNumEntry(QString("maxwinstreak%1").arg(_id),0);
+ if (n>m)
+ config->writeEntry(QString("maxwinstreak%1").arg(_id),n);
+ config->writeEntry(QString("loosestreak%1").arg(_id),0);
+ }
+
+ // sort cards by increasing z
+ QCanvasItemList list = canvas()->allItems();
+ QValueList<CardPtr> cards;
+ for (QCanvasItemList::ConstIterator it=list.begin(); it!=list.end(); ++it)
+ if ((*it)->rtti() == Card::RTTI) {
+ CardPtr p;
+ p.ptr = dynamic_cast<Card*>(*it);
+ assert(p.ptr);
+ cards.push_back(p);
+ }
+ qHeapSort(cards);
+
+ // disperse the cards everywhere
+ QRect can(0, 0, canvas()->width(), canvas()->height());
+ QValueList<CardPtr>::ConstIterator it = cards.begin();
+ for (; it != cards.end(); ++it) {
+ (*it).ptr->turn(true);
+ QRect p(0, 0, (*it).ptr->width(), (*it).ptr->height());
+ int x, y;
+ do {
+ x = 3*canvas()->width()/2 - kapp->random() % (canvas()->width() * 2);
+ y = 3*canvas()->height()/2 - (kapp->random() % (canvas()->height() * 2));
+ p.moveTopLeft(QPoint(x, y));
+ } while (can.intersects(p));
+
+ (*it).ptr->moveTo( x, y, 0, STEPS_WON);
+ }
+
+ bool demo = demoActive();
+ stopDemo();
+ canvas()->update();
+ emit gameWon(demo);
+}
+
+MoveHint *Dealer::chooseHint()
+{
+ if (hints.isEmpty())
+ return 0;
+
+ for (HintList::ConstIterator it = hints.begin(); it != hints.end(); ++it)
+ {
+ if ((*it)->pile()->target() && (*it)->dropIfTarget())
+ return *it;
+ }
+
+ return hints[randseq.getLong(hints.count())];
+}
+
+void Dealer::demo() {
+ if (waiting())
+ return;
+
+ if (stop_demo_next) {
+ stopDemo();
+ return;
+ }
+ stop_demo_next = false;
+ unmarkAll();
+ towait = (Card*)-1;
+ clearHints();
+ getHints();
+ demotimer->stop();
+
+ MoveHint *mh = chooseHint();
+ if (mh) {
+ // assert(mh->card()->source()->legalRemove(mh->card()));
+
+ CardList empty;
+ CardList cards = mh->card()->source()->cards();
+ bool after = false;
+ for (CardList::Iterator it = cards.begin(); it != cards.end(); ++it) {
+ if (*it == mh->card())
+ after = true;
+ if (after)
+ empty.append(*it);
+ }
+
+ assert(!empty.isEmpty());
+
+ int *oldcoords = new int[2*empty.count()];
+ int i = 0;
+
+ for (CardList::Iterator it = empty.begin(); it != empty.end(); ++it) {
+ Card *t = *it;
+ Q_ASSERT(!t->animated());
+ t->setAnimated(false);
+ t->turn(true);
+ oldcoords[i++] = int(t->realX());
+ oldcoords[i++] = int(t->realY());
+ }
+
+ assert(mh->card()->source() != mh->pile());
+ // assert(mh->pile()->legalAdd(empty));
+
+ mh->card()->source()->moveCards(empty, mh->pile());
+
+ i = 0;
+
+ for (CardList::Iterator it = empty.begin(); it != empty.end(); ++it) {
+ Card *t = *it;
+ int x1 = oldcoords[i++];
+ int y1 = oldcoords[i++];
+ int x2 = int(t->realX());
+ int y2 = int(t->realY());
+ t->move(x1, y1);
+ t->moveTo(x2, y2, int(t->z()), STEPS_DEMO);
+ }
+
+ delete [] oldcoords;
+
+ newDemoMove(mh->card());
+
+ } else {
+ Card *t = demoNewCards();
+ if (t) {
+ newDemoMove(t);
+ } else if (isGameWon()) {
+ canvas()->update();
+ emit gameWon(true);
+ return;
+ } else
+ stopDemo();
+ }
+
+ takeState();
+}
+
+Card *Dealer::demoNewCards()
+{
+ return 0;
+}
+
+void Dealer::newDemoMove(Card *m)
+{
+ towait = m;
+ connect(m, SIGNAL(stoped(Card*)), SLOT(waitForDemo(Card*)));
+}
+
+void Dealer::waitForDemo(Card *t)
+{
+ if (t == (Card*)-1)
+ return;
+ if (towait != t)
+ return;
+ t->disconnect();
+ towait = 0;
+ demotimer->start(250, true);
+}
+
+bool Dealer::isGameWon() const
+{
+ for (PileList::ConstIterator it = piles.begin(); it != piles.end(); ++it)
+ {
+ if (!(*it)->target() && !(*it)->isEmpty())
+ return false;
+ }
+ return true;
+}
+
+bool Dealer::isGameLost() const
+{
+ return false;
+}
+
+bool Dealer::checkRemove( int, const Pile *, const Card *) const {
+ return true;
+}
+
+bool Dealer::checkAdd( int, const Pile *, const CardList&) const {
+ return true;
+}
+
+void Dealer::drawPile(KPixmap &pixmap, Pile *pile, bool selected)
+{
+ QPixmap bg = myCanvas.backgroundPixmap();
+ QRect bounding(int(pile->x()), int(pile->y()), cardMap::CARDX(), cardMap::CARDY());
+
+ pixmap.resize(bounding.width(), bounding.height());
+ pixmap.fill(Qt::white);
+
+ if (!bg.isNull()) {
+ for (int x=bounding.x()/bg.width();
+ x<(bounding.x()+bounding.width()+bg.width()-1)/bg.width(); x++)
+ {
+ for (int y=bounding.y()/bg.height();
+ y<(bounding.y()+bounding.height()+bg.height()-1)/bg.height(); y++)
+ {
+ int sx = 0;
+ int sy = 0;
+ int dx = x*bg.width()-bounding.x();
+ int dy = y*bg.height()-bounding.y();
+ int w = bg.width();
+ int h = bg.height();
+ if (dx < 0) {
+ sx = -dx;
+ dx = 0;
+ }
+ if (dy < 0) {
+ sy = -dy;
+ dy = 0;
+ }
+ bitBlt(&pixmap, dx, dy, &bg,
+ sx, sy, w, h, Qt::CopyROP, true);
+ }
+ }
+ }
+
+
+ float s = -0.4;
+ float n = -0.3;
+
+ int mid = QMAX( QMAX(midColor().red(), midColor().green()), midColor().blue());
+
+ // if it's too dark - light instead of dark
+ if (mid < 120) {
+ s *= -1;
+ n = 0.4;
+ }
+
+ KPixmapEffect::intensity(pixmap, selected ? s : n);
+}
+
+int Dealer::freeCells() const
+{
+ int n = 0;
+ for (PileList::ConstIterator it = piles.begin(); it != piles.end(); ++it)
+ if ((*it)->isEmpty() && !(*it)->target())
+ n++;
+ return n;
+}
+
+void Dealer::setAnchorName(const QString &name)
+{
+ kdDebug(11111) << "setAnchorname " << name << endl;
+ ac = name;
+}
+
+QString Dealer::anchorName() const { return ac; }
+
+void Dealer::wheelEvent( QWheelEvent *e )
+{
+ QWheelEvent ce( viewport()->mapFromGlobal( e->globalPos() ),
+ e->globalPos(), e->delta(), e->state());
+ viewportWheelEvent(&ce);
+ if ( !ce.isAccepted() ) {
+ if ( e->orientation() == Horizontal && hScrollBarMode () == AlwaysOn )
+ QApplication::sendEvent( horizontalScrollBar(), e);
+ else if (e->orientation() == Vertical && vScrollBarMode () == AlwaysOn )
+ QApplication::sendEvent( verticalScrollBar(), e);
+ } else {
+ e->accept();
+ }
+}
+
+void Dealer::countGame()
+{
+ if ( !_gameRecorded ) {
+ kdDebug(11111) << "counting game as played." << endl;
+ KConfig *config = kapp->config();
+ KConfigGroupSaver kcs(config, scores_group);
+ unsigned int Total = config->readUnsignedNumEntry(QString("total%1").arg(_id),0);
+ ++Total;
+ config->writeEntry(QString("total%1").arg(_id),Total);
+ _gameRecorded = true;
+ }
+}
+
+void Dealer::countLoss()
+{
+ if ( _gameRecorded ) {
+ // update score
+ KConfig *config = kapp->config();
+ KConfigGroupSaver kcs(config, scores_group);
+ unsigned int n = config->readUnsignedNumEntry(QString("loosestreak%1").arg(_id),0) + 1;
+ config->writeEntry(QString("loosestreak%1").arg(_id),n);
+ unsigned int m = config->readUnsignedNumEntry(QString("maxloosestreak%1").arg(_id),0);
+ if (n>m)
+ config->writeEntry(QString("maxloosestreak%1").arg(_id),n);
+ config->writeEntry(QString("winstreak%1").arg(_id),0);
+ }
+}
+
+#include "dealer.moc"